2016-05-01 51 views
-1

我一直在挣扎了一下关于如何处理这种情况的路径:如何获得层表

我有一个表结构如下:

Family_code | Parent_Family_Code | .... 
    1     2 
    2     4 
    3     6 
    4     3 
    ...................... 

当用户搜索特定家族码,我需要返回的整个路径(最多10个级别最大),所以例如用于family_code = 1,我需要:

Family_code | parent_1 | p_2 | p_3 | p_4 | p_5 | ..... 
     1   2  4  3  6  null null..... 

我知道我可以使用sys_connect_by_path()这将带给我预期的结果,但作为一个字符串,而不是单独的列,这是我宁愿避免的。

这也可以通过10个左连接来完成同一个表,或者使用LEAD()/LAG()函数,这些函数将包含大量的子查询,并且会造成凌乱和难以理解的查询,但是再一次,这将会更加沉重那么它应该是,我需要尽可能简化它。

我想出了使用substr()功能的解决方案(代码的长度将永远是VARCHAR2(3)):

SELECT s.family_code, 
s.parent_family_code_1, 
s.parent_family_code_2, 
CASE WHEN length(s.family_path) - (4 * 3 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 3 + 2), 3) ELSE NULL END as parent_family_code_3, 
CASE WHEN length(s.family_path) - (4 * 4 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 4 + 2), 3) ELSE NULL END as parent_family_code_4, 
CASE WHEN length(s.family_path) - (4 * 5 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 5 + 2), 3) ELSE NULL END as parent_family_code_5, 
CASE WHEN length(s.family_path) - (4 * 6 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 6 + 2), 3) ELSE NULL END as parent_family_code_6, 
CASE WHEN length(s.family_path) - (4 * 7 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 7 + 2), 3) ELSE NULL END as parent_family_code_7, 
CASE WHEN length(s.family_path) - (4 * 8 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 8 + 2), 3) ELSE NULL END as parent_family_code_8, 
CASE WHEN length(s.family_path) - (4 * 9 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 9 + 2), 3) ELSE NULL END as parent_family_code_9, 
CASE WHEN length(s.family_path) - (4 * 10 + 2) > 0 THEN substr(s.family_path, length(s.family_path) - (4 * 10 + 2), 3) ELSE NULL END as parent_family_code_10 
    FROM (SELECT t.family_code, 
       t.parent_family_code as parent_family_code_1, 
       prior t.parent_family_code as parent_family_code_2, 
       sys_connect_by_path(t.family_code, ',') as family_path 
      FROM table t 
     connect by prior t.family_code = t.parent_family_code) s 

但我想没有,因为它使用的子串的解决方案当其他开发人员碰到它时,将很难做任何修改。 。 所以基本上我的问题是 - 如何在不使用子串的情况下选择整个路径作为不同的列?

+0

已编辑成问题的代码不工作 - 如果你也可以'SELECT family_path'然后你会看到(a)它没有得到整个路径,(b)由于你有可变长度的字符串并且使用了固定长度的条件,所以这个路径并不是全部被子字符串处理。 – MT0

+0

如果你想要整个路径,那么你可以使用'sys_connect_by_path(t.parent_family_code,',')|| ','|| t.family_code作为family_path',但它仍然不能解决案例条件中可变长度数据和固定长度的第二个问题。 – MT0

回答

0

稍微修改的查询从@MT0回答,使用PIVOT条款。

SELECT * 
FROM (
    select connect_by_root(family_code) as Family_code, 
      'P_' || level lev_el, 
      parent_family_code 
    from table_name t 
    start with not exists(
     select 1 from table_name t1 
     where t.family_code = t1.parent_family_code) 
    connect by prior parent_family_code = family_code 
) 
PIVOT (
    max(parent_family_code) 
    FOR (lev_el) IN ( 
     'P_1', 'P_2', 'P_3', 'P_4', 'P_5', 'P_6','P_7', 'P_8','P_9','P_10' , 
     'P_11', 'P_12', 'P_13', 'P_14', 'P_15', 'P_16','P_17', 'P_18','P_19','P_20', 
     'P_21', 'P_22', 'P_23', 'P_24', 'P_25', 'P_26','P_27', 'P_28','P_29','P_30' 
     /* add more "levels" here if required */ 
) 
); 

查询的样本数据来自@ MT0回答结果(@ MT0,感谢您提供的样本数据):

FAMILY_CODE  'P_1'  'P_2'  'P_3'  'P_4'  'P_5'  'P_6'  'P_7'  'P_8'  'P_9'  'P_10'  'P_11'  'P_12'  'P_13'  'P_14'  'P_15'  'P_16'  'P_17'  'P_18'  'P_19'  'P_20'  'P_21'  'P_22'  'P_23'  'P_24'  'P_25'  'P_26'  'P_27'  'P_28'  'P_29'  'P_30' 
--------------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 
       1   2   4   5   6                                                                        
       8   7   9   10   11                                                                        
+0

谢谢!这就是我一直在寻找的,没有想到一个数据透视解决方案。 – sagi

+0

你似乎已经使用了我的问题中的样本数据(因为你正在使用'START WITH NOT EXISTS',删除了'NULL'行) - 它只返回两行 - 合并两行以'1'开头的行和那行是不正确的'1,2,4,5,6' - '6'具有'3'的父亲而不是'5'。 – MT0

3

甲骨文设置

CREATE TABLE table_name (Family_code, Parent_Family_Code) AS 
SELECT 1, 2 FROM DUAL UNION ALL 
SELECT 2, 4 FROM DUAL UNION ALL 
SELECT 3, 6 FROM DUAL UNION ALL 
SELECT 6, NULL FROM DUAL UNION ALL 
SELECT 4, 3 FROM DUAL UNION ALL 
SELECT 4, 5 FROM DUAL UNION ALL 
SELECT 5, NULL FROM DUAL UNION ALL 
SELECT 8, 7 FROM DUAL UNION ALL 
SELECT 7, 9 FROM DUAL UNION ALL 
SELECT 9, 10 FROM DUAL UNION ALL 
SELECT 10, 11 FROM DUAL UNION ALL 
SELECT 11, NULL FROM DUAL; 

查询

SELECT TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth, NULL, 1)) AS family_code, 
     CASE WHEN max_depth > 1 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 1, NULL, 1)) END AS p1, 
     CASE WHEN max_depth > 2 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 2, NULL, 1)) END AS p2, 
     CASE WHEN max_depth > 3 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 3, NULL, 1)) END AS p3, 
     CASE WHEN max_depth > 4 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 4, NULL, 1)) END AS p4, 
     CASE WHEN max_depth > 5 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 5, NULL, 1)) END AS p5, 
     CASE WHEN max_depth > 6 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 6, NULL, 1)) END AS p6, 
     CASE WHEN max_depth > 7 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 7, NULL, 1)) END AS p7, 
     CASE WHEN max_depth > 8 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 8, NULL, 1)) END AS p8, 
     CASE WHEN max_depth > 9 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 9, NULL, 1)) END AS p9, 
     CASE WHEN max_depth > 10 THEN TO_NUMBER(REGEXP_SUBSTR(path, '/(\d+)', 1, max_depth - 10, NULL, 1)) END AS p10 
FROM (
    SELECT SYS_CONNECT_BY_PATH(Family_code, '/') AS path, 
     LEVEL AS max_depth 
    FROM table_name 
    WHERE CONNECT_BY_ISLEAF = 1 
    CONNECT BY PRIOR Family_Code = Parent_Family_Code 
    START WITH Parent_Family_Code IS NULL 
); 

输出

FAMILY_CODE   P1   P2   P3   P4   P5   P6   P7   P8   P9  P10 
----------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 
      1   2   4   5                    
      1   2   4   3   6                 
      8   7   9   10   11                 
+0

谢谢,我用'substr'完成了一些类似的事情,但我一直在想,如果没有搞错字符串,可能是一些层次结构的函数,connect_by_isleaf是不必要的,因为我需要每个人的结果,不只是叶子 – sagi