2016-07-23 29 views
1

Ich habe eine Oracle 12c Datenbank mit einer Tabelle mit baumartiger Struktur für Organisationseinheiten (Abteilungen und so weiter):SQL-Abfrage zurück entsprechende Datensätze mit ihren Vorfahren in baumartiger Struktur

CREATE TABLE "OUS" (
    "ID" NUMBER(38,0) NOT NULL ENABLE, 
    "NAME" VARCHAR2(255 CHAR) NOT NULL ENABLE, 
    "PARENT_ID" NUMBER(38,0), 
    PRIMARY KEY("ID"), 
    CONSTRAINT "OUS_HIERARCHY_FK" FOREIGN KEY ("PARENT_ID") REFERENCES "OUS" ("ID") ON DELETE CASCADE 
); 

So, mit der Struktur wie

| id | name   | parent_id | 
| -: | ------------- | --------: | 
| 1 | Root   | (NULL) | 
| 2 | Territorial 1 |   1 | 
| 3 | Regional 1-1 |   2 | 
| 4 | Alpha dept |   3 | 
| 5 | Beta dept  |   3 | 
| 6 | Regional 1-2 |   2 | 
| 7 | Gamma dept |   6 | 
| 8 | Delta dept |   7 | 
| 9 | Territorial 2 |   1 | 
| 10 | Regional 2-1 |   9 | 
| 11 | Epsilon dept |  10 | 
| 12 | Zeta dept  |  10 | 

Sie können es schaffen mit SQL wie:

INSERT INTO ous (id, name, parent_id) VALUES (13, 'Root',   NULL); 
INSERT INTO ous (id, name, parent_id) VALUES ( 2, 'Territorial 1', 13); 
INSERT INTO ous (id, name, parent_id) VALUES ( 1, 'Regional 1-1',  2); 
INSERT INTO ous (id, name, parent_id) VALUES ( 5, 'Alpha dept',  1); 
INSERT INTO ous (id, name, parent_id) VALUES ( 4, 'Beta dept',  1); 
INSERT INTO ous (id, name, parent_id) VALUES ( 6, 'Regional 1-2',  2); 
INSERT INTO ous (id, name, parent_id) VALUES ( 7, 'Gamma dept',  6); 
INSERT INTO ous (id, name, parent_id) VALUES ( 8, 'Delta dept',  6); 
INSERT INTO ous (id, name, parent_id) VALUES ( 9, 'Territorial 2', 13); 
INSERT INTO ous (id, name, parent_id) VALUES ( 3, 'Regional 2-1',  9); 
INSERT INTO ous (id, name, parent_id) VALUES (15, 'Epsilon dept',  3); 
INSERT INTO ous (id, name, parent_id) VALUES (12, 'Zeta dept',  3); 

Ich muss einige der Organisationseinheiten finden, die bestimmte Kriterien erfüllen (wie name = 'Alpha' OR name = 'Epsilon) und einen Teilbaum dieser OU und ihrer Vorfahren erhalten.

Zum Beispiel:

| id | name   | parent_id | 
| -: | ------------- | --------: | 
| 1 | Root   | (NULL) | ← Ancestor of Alpha and Epsilon 
| 2 | Territorial 1 |   1 | ← Ancestor of Alpha 
| 3 | Regional 1-1 |   2 | ← Ancestor of Alpha 
| 4 | Alpha dept |   3 | ← Matches the WHERE clause! 
| 9 | Territorial 2 |   1 | ← Ancestor of Epsilon 
| 10 | Regional 2-1 |   9 | ← Ancestor of Epsilon 
| 11 | Epsilon dept |  10 | ← Matches the WHERE clause! 

ich an verschiedenen Hierarchical and recursive queries in SQL sah: Oracle Hierarchical queries und CTEs, kann aber nicht eine Abfrage herausfinden, die mir ein solches Ergebnis zurückgeben kann.

Ich verwende Oracle Database 12c.

Ich habe versucht, Anfragen wie:

SELECT ous.* FROM ous 
WHERE name = 'Alpha' OR name = 'Epsilon' 
START WITH 
    parent_id IS NULL 
CONNECT BY 
    PRIOR id = parent_id 
ORDER SIBLINGS BY name; 

Aber es gibt 0 Zeilen wie WHERE auf alle Zeilen angewendet wird (so Vorfahren gefiltert werden)

Auch habe ich versucht:

WITH RECURSIVE all_nodes (id, parent_id, name) AS (
    SELECT ous.id, ous.parent_id, name FROM ous WHERE (name = 'Alpha' OR name = 'Epsilon') 
    UNION 
    SELECT ous.id, ous.parent_id, name FROM ous INNER JOIN all_nodes ON ous.parent_id = all_nodes.id 
) 
SELECT * FROM all_nodes INNER JOIN ous ON all_nodes.id = ous.id ORDER BY name; 

Aber es gibt Fehler SQL Error [905] [42000]: ORA-00905: keyword is missing

Antwort

3

Sie können dies tun mit einer rekursiven WG:

with t(name, id, parent_id) as (
     select name, id, parent_id 
     from ous 
     where name in ('alpha', 'epsilon') 
     union all 
     select ous.name, ous.id, ous.parent_id 
     from t join 
      ous 
      on ous.id = t.parent_id 
    ) 
select distinct t.id, t.name, t.parent 
from t 
order by t.id; 

Die select distinct ist wahrscheinlich nicht notwendig.

Rekursive CTEs haben den Vorteil, dass sie Standard-SQL sind. Daher wird die Logik von vielen verschiedenen Datenbanken unterstützt.

+0

Vielen Dank für die schnelle Antwort. Leider gibt Ihre Abfrage nur übereinstimmende Zeilen ohne ihre Vorfahren zurück. Auch Oracle sagt, dass rekursive CTE eine Alias-Liste (ORA-32039) haben muss, also habe ich die erste Zeile in 'mit t (name, id, parent_id) als (') geändert. – Envek

+0

Es wurde herausgefunden: die Gleichheit in ON-Klausel sollte umgekehrt werden: auf ous.id = t.parent_id. Jetzt funktioniert es! – Envek

+0

Ist es möglich, die Ergebnisse nach Hierarchie und Namen zu sortieren, falls die IDs nicht von Eltern zu Kindern wachsen, wie zB ORDER SIBLINGS BY in der Oracle-Syntax? – Envek

2

Natürlich können Sie hierfür eine hierarchische Abfrage verwenden. Das Problem ist, dass Sie, wenn Sie mit mehreren Blättern beginnen, irgendwann doppelte Zeilen erhalten. Sie können die Duplikate mit "distinct" entfernen, dies beeinträchtigt jedoch die Leistung, insbesondere auf einem sehr großen Tisch oder wenn Sie mit zu vielen Blättern beginnen. Am Ende des Tages ist eine rekursive Abfrage oft schwieriger zu schreiben, aber effizienter als eine hierarchische Abfrage.

Der Vollständigkeit halber ist hier die hierarchische Lösung. Verwenden der EMP-Tabelle im SCOTT-Schema zur Veranschaulichung. Zuerst wird die gerade hierarchische Abfrage (und die Duplikate in der Ausgabe) und dann die Version mit "distinct" angezeigt.

select empno, mgr 
from scott.emp 
start with empno in (7902, 7788) 
connect by prior mgr = empno 
; 

    EMPNO  MGR 
---------- ---------- 
     7788  7566 
     7566  7839 
     7839 
     7902  7566 
     7566  7839 
     7839 


select distinct empno, mgr 
from scott.emp 
start with empno in (7902, 7788) 
connect by prior mgr = empno 
; 

    EMPNO  MGR 
---------- ---------- 
     7839 
     7566  7839 
     7902  7566 
     7788  7566 
+0

Danke! Ich war erfolgreich mit der Abfrage 'SELECT DISTINCT ous. * FROM ous START mit dem Namen LIKE' Alpha% 'ODER mit dem Namen LIKE' Epsilon% 'CONNECT BY PRIOR parent_id = ID ORDER SIBLINGS NACHNAME; 'aber die Reihenfolge der Datensätze ist seltsam. Kann damit etwas gemacht werden? Ich habe 'CREATE TABLE' und' INSERT' zur Frage hinzugefügt. – Envek

+0

Was ist die gewünschte Reihenfolge? Beachten Sie, dass Einschränkungen in der ORDER BY-Klausel einer hierarchischen Abfrage bestehen, wenn Sie das Schlüsselwort DISTINCT in der SELECT-Klausel verwenden. Beispielsweise könnten Sie ORDER BY LEVEL DESC verwenden, wenn Sie den Stamm am oberen Rand haben möchten. Dazu müssen Sie jedoch die Pseudospalte LEVEL in die SELECT-Klausel aufnehmen. – mathguy