Oracle合并缺少层的层次结构

x4shl7ld  于 2022-11-03  发布在  Oracle
关注(0)|答案(3)|浏览(188)

我需要在Oracle 12c中扁平化一个层次结构,简化的输入示例在左侧,底部添加了一个图形表示(下面的屏幕截图)。目标输出在右侧突出显示。
有4个层次,其中Lvl_1是层次结构的顶层,并且有缺失的层次。要求是缺失的层次应使用可用的最低明细层填充。有多个样本,如P_1_2
我不知道如何解决这个问题,我已经找到了几个针对SQL Server而不是Oracle的解决方案,但没有一个解决方案能像此要求那样管理缺少的级别标准
有谁做过这样的事吗?

CREATE TABLE ztest_product
(
    PRODUCT_ID VARCHAR(10)
    , PARENT_ID VARCHAR(10)
)
;

Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_0', NULL);
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1', 'P_0');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1_1', 'P_1');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1_2', 'P_1');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1_1_1', 'P_1_1');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1_1_2', 'P_1_1');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_1_1_3', 'P_1');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_2_1_1', 'P_0');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_3', 'P_0');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_3_1_1', 'P_3');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_3_2', 'P_3');
Insert into ztest_product(PRODUCT_ID, PARENT_ID) values ('P_3_1_2', 'P_3_2');

COMMIT;

eanckbw9

eanckbw91#

您可以找到层次结构的叶节点,并使用SYS_CONNECT_BY_PATH找到所采用的路径,然后将其拆分为不同的级别:

SELECT lvl_1,
       COALESCE(lvl_2, lvl_1) AS lvl_2,
       COALESCE(lvl_3, lvl_2, lvl_1) AS lvl_3,
       COALESCE(lvl_4, lvl_3, lvl_2, lvl_1) AS lvl_4
FROM   (
  SELECT REGEXP_SUBSTR(
           SYS_CONNECT_BY_PATH(product_id, ','), ',([^,]+)', 1, 1, NULL, 1
         ) AS lvl_1,
         REGEXP_SUBSTR(
           SYS_CONNECT_BY_PATH(product_id, ','), ',([^,]+)', 1, 2, NULL, 1
         ) AS lvl_2,
         REGEXP_SUBSTR(
           SYS_CONNECT_BY_PATH(product_id, ','), ',([^,]+)', 1, 3, NULL, 1
         ) AS lvl_3,
         REGEXP_SUBSTR(
           SYS_CONNECT_BY_PATH(product_id, ','), ',([^,]+)', 1, 4, NULL, 1
         ) AS lvl_4
  FROM   ztest_product
  WHERE  CONNECT_BY_ISLEAF = 1
  START WITH parent_id IS NULL
  CONNECT BY PRIOR product_id = parent_id
)

对于您的示例数据,它将输出:
| 第1级|第二级|三级|四级|
| - -|- -|- -|- -|
| 第0页|第一页|第1页第1页|第1页第1页|
| 第0页|第一页|第1页第1页|第1_1_2页|
| 第0页|第一页|第1_1_3页|第1_1_3页|
| 第0页|第一页|第1页第2页|第1页第2页|
| 第0页|第二页第一页|第二页第一页|第二页第一页|
| 第0页|第三层|第3_1_1页|第3_1_1页|
| 第0页|第三层|P_3_2页|第三页第一页第二页|
fiddle

oewdyzsn

oewdyzsn2#

下面是一个准备查询,它生成缺少的节点,而不使用regexp_substr,如果数据集很大,regexp_substr可能会导致性能问题:

WITH cnodes AS (
    SELECT LEVEL AS lvl, p.*, connect_by_isleaf AS is_leaf FROM ztest_product p
    START WITH product_id IN (
        SELECT product_id FROM ztest_product WHERE parent_id IS NULL
    )
    CONNECT BY PRIOR product_id = parent_id 
),
inodes AS (
    SELECT cn.* FROM cnodes cn
    WHERE NOT EXISTS(
        SELECT 1 FROM cnodes c1 WHERE cn.product_id = c1.parent_id AND cn.lvl = c1.lvl + 1 AND cn.lvl < 4
    )
    AND is_leaf = 1 AND lvl < 4
)
SELECT * FROM (
    SELECT lvl, product_id, parent_id FROM (
        SELECT lvl+LEVEL AS lvl, product_id, product_id AS parent_id FROM inodes
        CONNECT BY LEVEL < 5 - lvl AND PRIOR product_id = product_id AND PRIOR sys_guid() IS NOT NULL
        UNION ALL 
        SELECT lvl, product_id, parent_id FROM cnodes
    )
)
ORDER BY lvl, parent_id, product_id
;

结果是包含缺失节点的完整树,其中添加的节点是其自身的父节点:

xtupzzrd

xtupzzrd3#

完整查询给出了预期的表:

WITH cnodes AS (
    SELECT LEVEL AS lvl, p.*, connect_by_isleaf AS is_leaf, SYS_CONNECT_BY_PATH(product_id,'/') AS p 
    FROM ztest_product p
    START WITH product_id IN (
        SELECT product_id FROM ztest_product WHERE parent_id IS NULL
    )
    CONNECT BY PRIOR product_id = parent_id 
),
inodes AS (
    SELECT cn.* FROM cnodes cn
    WHERE NOT EXISTS(
        SELECT 1 FROM cnodes c1 WHERE cn.product_id = c1.parent_id AND cn.lvl = c1.lvl + 1 AND cn.lvl < 4
    )
    AND is_leaf = 1 AND lvl < 4
),
fnodes AS (
    SELECT lvl, product_id, parent_id, p
    FROM (
        SELECT lvl+LEVEL AS lvl, product_id, product_id AS parent_id, p || SYS_CONNECT_BY_PATH(product_id,'/') AS p
        FROM inodes
        CONNECT BY LEVEL < 5 - lvl AND PRIOR product_id = product_id AND PRIOR sys_guid() IS NOT NULL
        UNION ALL 
        SELECT lvl, product_id, parent_id, p FROM cnodes
    )
)
SELECT * FROM (
    SELECT f.r AS row_idx, l AS col_idx, pitem FROM (
        SELECT ROW_NUMBER() OVER(ORDER BY p) AS r, f.* 
        FROM fnodes f
        WHERE lvl = 4
    ) f
    CROSS APPLY (
        SELECT LEVEL AS l, regexp_substr( f.p , '[^/]+', 1, LEVEL) AS pitem FROM DUAL
        CONNECT BY regexp_substr( f.p , '[^/]+', 1, LEVEL) IS NOT NULL
    ) t
)
PIVOT (
    MAX(pitem) AS product_id FOR col_idx IN (1,2,3,4)
)
;

相关问题