2016-05-01 28 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