2016-11-04 1 views
0

In den letzten Tagen arbeite ich an einer Lösung für meine Aufgabe und ich kann einfach keine Antwort finden.Oracle SQL connect_by_root und Unterabfragen

Einführung: Angenommen, wir haben eine einzige Tabelle namens People. Jede Person hat drei Felder: name, boss und position. name ist ein Primärschlüssel, position nur eine Zeichenfolge und boss als Fremdschlüssel verweist auf eine andere Person name. Es erstellt einen normalen Baum wie Person1 -> Person2 -> Person3 -> Person4, wobei Person4 ein höchster Chef und Person1 die Wurzel ist. Zur Vereinfachung nehmen wir an, dass niemand mehr als 3 Bosse hat, und Person4 ist der Kopf Chef

Beispiel Wege (die einzige Person mit boss gleich null.):

Person7 -> Person4

Person6 -> Person8 -> Person4

Person2 -> Person8 -> Person4

So meine Aufgabe sagt: eine Abfrage erstellen, die in hierarchischer ord zeigt er Namen von jedem Vorsprung für jede Person mit position gleich „Arbeiter“ oder „Manager“ nur Baumoperationen unter Verwendung (eine Verbindung durch, connect_by_root etc.) und Unterabfragen

die Ausgabetabelle von 5 Spalten bestehen muss:

Wenn eine Boss-Spalte null wäre, dann sollte ich einige Leerzeichen einfügen.

Das ist meine Abfrage so weit:

select 
    case  
    when l = 1 then name else ' ' end as "Name", 
    position, 
    case 
    when l = 2 then name else ' ' end as "Boss 1", 
    case 
    when l = 3 then name else ' ' end as "Boss 2", 
    case 
    when l = 4 then name else ' ' end as "Boss 3"  
from (
    select 
    connect_by_root position as position, 
    level as l, 
    name 
    from 
    People 
    connect by prior 
    boss = name 
    start with 
    position = 'Worker' 
    or position = 'Manager' 
); 

es irgendwie funktioniert der Trick, aber jeder Ebene des Baumes ist eine neue Reihe, das ist etwas, was ich vermeiden müssen. Ich weiß, warum diese Abfrage ein Ergebnis wie dieses erzeugt, aber ich habe keine Ahnung, wie ich den Baum durchqueren kann, ohne bei jedem Schritt eine neue Zeile zu erstellen.

Mein Ergebnis:

Name |Position|Boss 1|Boss 2|Boss 3 

JOHN WORKER     
     WORKER HENRY    
     WORKER   PETER  
TERRY WORKER      
     WORKER PETER    
ALICE WORKER     
     WORKER PETER    
BILL MANAGER      
     MANAGER JAMES    
     MANAGER   PETER  

Dies ist das Ergebnis, das ich erreichen möchte:

Name |Position|Boss 1|Boss 2|Boss 3 

JOHN WORKER HENRY PETER   

TERRY WORKER PETER     

ALICE WORKER PETER     

BILL MANAGER JAMES PETER     

Gibt es eine Lösung ohne anspruchsvolle Funktionen wie Pivot mit ihm arbeiten zu machen?

+0

Nur 'max' oder' min' Funktionen. Und ersetze '... name else '' end ...' mit '... name else null end ...' – Dmitry

+0

Diese Funktionen in Unterabfrage? – Karatte

Antwort

0

Es sollte so funktionieren (ich keine Quelldaten zu überprüfen):

select 
    root_name as "Name", 
    max(position), 
    max(case when l = 2 then name else null end) as "Boss 1", 
    max(case when l = 3 then name else null end) as "Boss 2", 
    max(case when l = 4 then name else null end) as "Boss 3"  
from (
    select 
    connect_by_root position as position, 
    connect_by_root name as root_name, 
    level as l, 
    name 
    from 
    People 
    connect by prior 
    boss = name 
    start with 
    position = 'Worker' 
    or position = 'Manager' 
) 
group by root_name; 
+0

Sie vermissen eine Gruppenklausel, plus das ist eine Form der Drehung, die der OP meiner Meinung nach nicht besonders machen wollte. – Boneist

+1

@Boneimer Oh, danke! Ich habe versucht, in der ursprünglichen Abfrage minimale Korrekturen vorzunehmen. – Dmitry

+0

Funktioniert wie ein Zauber, jetzt muss ich über eine Lösung nachdenken. Was macht die "max" -Funktion? Ist es nur ein Workaround, um 'root_name 'gruppieren zu können? Macht es so etwas wie "Wähle Max von Null und Namen" und gibt immer den Namen zurück? – Karatte

0

Es ist möglich, dies zu tun, ohne schwenkt usw. benötigt, rein CONNECT_BY_ROOT Verwendung CONNECT_BY_ISLEAF und SYS_CONNECT_BY_PATH sowie vernünftige Nutzung REGEXP_SUBSTR:

WITH people AS (SELECT 'JOHN' name, 'WORKER' position, 'HENRY' boss FROM dual UNION ALL 
       SELECT 'HENRY' name, 'CFO' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'TERRY' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'ALICE' name, 'WORKER' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'JAMES' name, 'CIO' position, 'PETER' boss FROM dual UNION ALL 
       SELECT 'FRED' name, 'MANAGER' position, NULL boss FROM dual UNION ALL 
       SELECT 'BILL' name, 'MANAGER' position, 'JAMES' boss FROM dual UNION ALL 
       SELECT 'PETER' name, 'CEO' position, 'FRED' boss FROM dual) 
-- end of mimicking your people table with some sample data in it 
-- you wouldn't need the above, just use the query below: 
SELECT connect_by_root name AS name, 
     connect_by_root position AS position, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 1) boss1, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 2) boss2, 
     regexp_substr(sys_connect_by_path(boss, '>'), '[^>]+', 1, 3) boss3 
FROM people 
WHERE connect_by_isleaf = 1 
CONNECT BY PRIOR boss = name 
START WITH position IN ('WORKER', 'MANAGER'); 

NAME POSITION BOSS1 BOSS2 BOSS3 
----- -------- ----- ----- ----- 
ALICE WORKER PETER FRED 
BILL MANAGER JAMES PETER FRED 
FRED MANAGER    
JOHN WORKER HENRY PETER FRED 
TERRY WORKER PETER FRED 

CONNECT_BY_ISLEAF bestimmt, ob die Zeile eine Blattreihe (1) oder nicht (0). Daher können Sie, wie Sie den Stammwert mithilfe von CONNECT_BY_ROOT identifizieren können, mithilfe von CONNECT_BY_ISLEAF feststellen, welche Zeile eine Endzeile ist.

SYS_CONNECT_BY_PATH generiert einen Pfad aller Werte, die es bis jetzt erreicht hat. In den Blattzeilen enthält es also alle notwendigen Werte. Wir können dann diese generierte Zeichenfolge analysieren, um den ersten, zweiten usw. Teil zu erhalten, der den Pfadbegrenzer nicht enthält.