2009-07-17 9 views
2

Ich habe einen Baum in meiner Datenbank, die mit Eltern-ID-Links gespeichert ist.SQL Finden Sie alle direkten Nachkommen in einem Baum

Eine Probe von dem, was ich für Daten in der Tabelle haben, ist:

 
id | name  | parent id 
---+-------------+----------- 
0 | root   | NULL 
1 | Node 1  | 0 
2 | Node 2  | 0 
3 | Node 1.1 | 1 
4 | Node 1.1.1| 3 
5 | Node 1.1.2| 3 

Nun möchte ich eine Liste aller direkten Nachkommen eines gegebenen Knotens zu bekommen, aber wenn keine vorhanden Ich habe möchte Es gibt nur den Knoten selbst zurück.

möchte ich die Rückkehr für die Abfrage für Kinder von id = 3 sein:

 
children 
-------- 
4 
5 

Dann wird die Abfrage für die Kinder von id = 4 sein:

 
children 
-------- 
4 

Ich kann ändern die Art, wie ich den Baum in einem verschachtelten Satz ablege, aber ich sehe nicht, wie das die Anfrage möglich machen würde.

+0

Ich weiß, wie dies mit Cursors oder CTEs tun, aber das ist SQL Server. – TheTXI

Antwort

6

In neuen PostgreSQL 8.4 Sie können es tun, ein mit CTE:

WITH RECURSIVE q AS 
     (
     SELECT h, 1 AS level, ARRAY[id] AS breadcrumb 
     FROM t_hierarchy h 
     WHERE parent = 0 
     UNION ALL 
     SELECT hi, q.level + 1 AS level, breadcrumb || id 
     FROM q 
     JOIN t_hierarchy hi 
     ON  hi.parent = (q.h).id 
     ) 
SELECT REPEAT(' ', level) || (q.h).id, 
     (q.h).parent, 
     (q.h).value, 
     level, 
     breadcrumb::VARCHAR AS path 
FROM q 
ORDER BY 
     breadcrumb 

für Details Diesen Artikel in meinem Blog sehen:

In 8.3 oder früher, Sie muss eine funktion schreiben:

CREATE TYPE tp_hierarchy AS (node t_hierarchy, level INT); 

CREATE OR REPLACE FUNCTION fn_hierarchy_connect_by(INT, INT) 
RETURNS SETOF tp_hierarchy 
AS 
$$ 
     SELECT CASE 
       WHEN node = 1 THEN 
         (t_hierarchy, $2)::tp_hierarchy 
       ELSE 
         fn_hierarchy_connect_by((q.t_hierarchy).id, $2 + 1) 
       END 
     FROM (
       SELECT t_hierarchy, node 
       FROM (
         SELECT 1 AS node 
         UNION ALL 
         SELECT 2 
         ) nodes, 
         t_hierarchy 
       WHERE parent = $1 
       ORDER BY 
         id, node 
       ) q; 
$$ 
LANGUAGE 'sql'; 

und aus dieser Funktion:

SELECT * 
FROM fn_hierarchy_connect_by(4, 1) 

Der erste Parameter ist die Wurzel id sollte die zweite 1 sein. Weitere Einzelheiten

diesen Artikel in meinem Blog sehen:

Update:

Um nur die erste Stufe Kinder, oder der Knoten selbst wenn die Kinder zu zeigen, nicht vorhanden, geben Sie diese Abfrage aus:

SELECT * 
FROM t_hierarchy 
WHERE parent = @start 
UNION ALL 
SELECT * 
FROM t_hierarchy 
WHERE id = @start 
     AND NOT EXISTS 
     (
     SELECT NULL 
     FROM t_hierarchy 
     WHERE parent = @start 
     ) 

Dies ist effizienter als ein JOIN, da die zweite Abfrage höchstens zwei Index-Scans benötigt: Der erste, um herauszufinden, ob ein Kind existiert, der zweite, um die Eltern-Zeile auszuwählen, wenn keine Kinder existieren .

+0

Das Problem dabei ist, dass ich wollte, dass es nur die ersten Level-Kinder zeigt und wenn es keine gibt, gebe nur den Knoten selbst zurück. Ich antwortete mit einer Antwort, die für mich funktioniert, nachdem ich noch ein bisschen mehr herumgespielt habe. – tatsu

+0

+1 Große Antwort! Ich habe gerade mit diesem Problem zu kämpfen, und würde es begrüßen, wenn Sie nur bestätigen könnten, dass dies immer noch der Weg ist, wie Sie das Problem in der neuesten PostgreSQL-Version angehen würden. Idealerweise suche ich nach einer Lösung, die sowohl in PosgreSQL als auch in H2 – Jack

+0

@JacobusR funktioniert: die erste Abfrage ist, was Sie in 9.2 verwenden sollten – Quassnoi

0

Eine Abfrage gefunden, die so funktioniert, wie ich es wollte.

 
SELECT * FROM 
    (SELECT id FROM t_tree WHERE name = '') AS i, 
    t_tree g 
WHERE 
    ((i.id = g.id) AND 
     NOT EXISTS (SELECT * FROM t_tree WHERE parentid = i.id)) OR 
    ((i.id = g.parentid) AND 
     EXISTS (SELECT * FROM t_tree WHERE parentid = i.id)) 
Verwandte Themen