2016-05-01 7 views
-1

ich ein wenig über zu kämpfen habe, wie man mit dieser Situation umgehen:Wie den Pfad einer Hierarchietabelle erhalten

Ich habe eine Tabelle wie folgt strukturiert:

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

Wenn ein Benutzer für eine bestimmte Familie Code suchen, muss ich den ganzen Weg zurück (bis zu 10 Ebenen max), so zum Beispiel für family_code = 1 ich brauche werde:

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

ich weiß, ich sys_connect_by_path() verwenden können Das bringt mir das erwartete Ergebnis aber als String, und nicht als separate Spalten, was ich lieber vermeiden möchte.

Dies kann auch mit 10 linken Verknüpfungen zu der gleichen Tabelle oder die Verwendung von LEAD()/LAG() Funktionen, die eine Menge von Unterabfragen enthalten wird und eine unordentliche und unleserliche Abfrage machen, aber wiederum wird dies schwerer sein dann sollte es sein und ich muss es so vereinfachen wie ich kann.

Ich habe mit einer Lösung mit substr() Funktion kommen (die Länge der Codes immer varchar2 sein (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 

Aber ich würde, da es eine Lösung ohne die Verwendung von Teil mag es wird schwieriger sein, irgendwas daran zu ändern, wenn andere Entwickler es berühren werden . Also meine Frage ist im Grunde - wie wähle ich den gesamten Pfad als verschiedene Spalten ohne die Verwendung von Substrings?

+0

Der Code, den Sie in die Frage bearbeitet haben nicht funktioniert - wenn Sie werden auch 'SELECT family_path' sehen, dann sehen Sie, dass (a) es nicht den gesamten Pfad erhält und (b) der Pfad nicht vollständig von den Sub-Strings verarbeitet wird, da Sie Strings variabler Länge haben und Fixed-Length-Bedingungen verwenden. – MT0

+0

Wenn Sie den gesamten Pfad haben möchten, können Sie 'sys_connect_by_path (t.parent_family_code, ',') || verwenden ',' || t.family_code als family_path', löst aber immer noch nicht die zweite Ausgabe von Daten variabler Länge und fester Längen in den Fallbedingungen. – MT0

Antwort

0

Eine leicht modifizierte Abfrage von @ MT0 antworte mit PIVOT Klausel.

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 */ 
) 
); 

Ein Ergebnis der Abfrage für Probendaten von @ MT0 Antwort (@ MT0, danke Beispieldaten für die Bereitstellung):

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

Danke! das war, was ich suchte, dachte nicht an eine Pivot-Lösung. – sagi

+0

Sie scheinen die Beispieldaten aus meiner Frage verwendet zu haben (mit den 'NULL' Zeilen entfernt, da Sie' START WITH NOT EXISTS' verwenden) - es gibt nur zwei Zeilen zurück - beide Zeilen zusammenfassen, die mit '1' und dieser Zeile beginnen ist falsch '1,2,4,5,6' -' 6' hat Eltern von '3' nicht' 5'. – MT0

3

Oracle-Setup:

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; 

Abfrage:

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 
); 

Output:

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

Danke, ich habe etwas Ähnliches mit 'substr' gemacht, aber ich habe mich gefragt, ob es möglich ist, ohne eine Zeichenfolge zu verwirren, vielleicht einige hierarchische eingebaute Funktionen, auch die connect_by_isleaf ist unnötig, da ich die Ergebnisse für alle brauche nicht nur die Blätter – sagi

Verwandte Themen