2009-06-19 3 views
2

Ich brauche ein bisschen Hilfe beim Erstellen einer Abfrage, die mich die folgenden Daten filtern lassen wird.Herausfiltern von Kindern in einer Tabelle mit Parent-ID

Table: MyTree 
Id ParentId Visible 
===================== 
1 null  0 
2 1   1 
3 2   1 
4 3   1 
5 null  1 
6 5   1 

Ich erwarte, dass das folgende Ergebnis aus der Abfrage:

Id ParentId Visible 
===================== 
5 null  1 
6 5   1 

Das heißt, alle Kinder des verborgenen Knoten sollen nicht zurückgegeben werden. Darüber hinaus ist die Tiefe einer Hierarchie nicht begrenzt. Jetzt antworte nicht "Just Set 2, 3 & 4 zu sichtbar = 0" aus nicht offensichtlichen Gründen, die nicht möglich ist ... Wie ich ein schreckliches "Legacy-System" repariere.

Ich dachte an so etwas wie:

SELECT * 
FROM MyTree m1 
JOIN MyTree m2 ON m1.ParentId = m2.Id 
WHERE m1.Visible = 1 
AND (m1.ParentId IS NULL OR m2.Id IS NOT NULL) 

Sorry für den syntaktischen Fehler

Aber das wird nur die erste Stufe filtert, nicht wahr? Hoffe du kannst helfen.

Bearbeiten: Beendet den Titel, hoppla. Der Server ist ein brandneuer MSSQL 2008-Server, aber die Datenbank wird im Kompatibilitätsmodus 2000 ausgeführt.

+1

Welche Version von SQL Server? –

+0

@JohannesH - Ich bin mir nicht sicher, ob ich verstehe, was Sie erreichen wollen. –

+0

Die Antwort, die ich denke, hängt ziemlich stark davon ab, ob Sie in SQL Server 2005+ (die rekursive CTEs tun können) oder SQL Server 2000- (die nicht möglich) – AakashM

Antwort

2

ich mit @ Quassnoi Fokus auf rekursive CTE (in SQL Server 2005 oder höher), aber ich denke, die Logik zustimmen unterscheidet sich die ursprüngliche Frage zu beantworten:

WITH visall(id, parentid, visible) AS 
    (SELECT id, parentid, visible 
    FROM mytree 
    WHERE parentid IS NULL 
     UNION ALL 
    SELECT m.id, m.parentid, m.visible & visall.visible AS visible 
    FROM visall 
    JOIN mytree m 
     ON m.parentid = visall.id 
    ) 
SELECT * 
FROM visall 
WHERE visall.visible = 1 

A wahrscheinlich optimiert Weise die gleiche Logik zum Ausdruck bringen sollte haben sein, die sichtbaren Kontrollen in der WHERE so viel wie möglich - so schnell wie möglich Rekursion entlang unsichtbar „Teilbäume“ stoppen. Dh:

WITH visall(id, parentid, visible) AS 
    (SELECT id, parentid, visible 
    FROM mytree 
    WHERE parentid IS NULL AND visible = 1 
     UNION ALL 
    SELECT m.id, m.parentid, m.visible 
    FROM visall 
    JOIN mytree m 
     ON m.parentid = visall.id 
    WHERE m.visible = 1 
    ) 
SELECT * 
FROM visall 

Wie bei Performance-Problemen üblich, beide Versionen auf realistischen Daten Benchmarking erforderlich ist, um das Vertrauen zu entscheiden (es hilft auch, zu überprüfen, ob sie produzieren tatsächlich identische Ergebnisse ;-) - wie DB-Motoren Optimizern manchmal komische Dinge aus seltsamen Gründen ;-).

+0

@Alex: Ich glaube, du kamst dem am nächsten, was ich suchte. Also akzeptiere deine Antwort. – JohannesH

+0

Ihre Lösung durchquert die unsichtbaren Knoten und filtert sie dann aus. Ist es nicht besser, die unsichtbaren Knoten direkt im CTE herauszufiltern, um einige Rekursionsschritte zu speichern? – Quassnoi

+0

@JohannesH, froh zu helfen. Ja, @Quassnoi, ich bin mir sicher, dass mein Code verbessert werden kann (verschiebe die sichtbaren Checks auf WHERE statt auf SELECT in beiden Zweigen der recursion shd help - lemme edit, um so optimierten Code hinzuzufügen), aber spezielle "id = 5 "ist nicht, was der Fragesteller im Sinn hatte, nur ein Beispiel. –

2

In SQL Server 2005+:

WITH q (id, parentid, visible) AS 
     (
     SELECT id, parentid, visible 
     FROM mytree 
     WHERE id = 5 
     UNION ALL 
     SELECT m.id, m.parentid, m.visible 
     FROM q 
     JOIN mytree m 
     ON  m.parentid = q.id 
     WHERE q.visible = 1 
     ) 
SELECT * 
FROM q 
-1

Ich glaube nicht, was Sie brauchen, ist möglich, aus einer einzigen Abfrage. Dies sieht eher so aus, als ob etwas aus dem Code zu tun ist und dennoch werden mehrere Abfragen an die Datenbank benötigt.

Wenn Sie es wirklich von SQL aus tun müssen, wäre Ihre beste Wette, einen Cursor zu verwenden und eine Tabelle mit versteckten IDs zu erstellen. Wenn sich die Daten nicht häufig ändern, können Sie diese "temporäre" Tabelle als eine Art Cache speichern.

bearbeiten: Ich korrigierte stehen (für SQL 2005) und auch gelernt, etwas Neues heute :)

+0

Ich stimme völlig zu, dass es im Code der App gemacht werden sollte, aber leider habe ich nicht die Quelle. Saugt, um Erbe zu erhalten. ;) – JohannesH

1

Ich denke Quassnoi war nah an dem, was der Fragesteller will, aber nicht ganz. Ich denke, das ist es, was die Fragesteller für (SQL Server 2005+) suchen:

WITH q (id) AS 
     (
     SELECT id 
     FROM mytree 
     WHERE parentid is null and visible=1 
     UNION ALL 
     SELECT m.id 
     FROM q 
     JOIN mytree m 
     ON  m.parentid = q.id 
     WHERE q.visible = 1 
     ) 
SELECT * 
FROM q 

Common Table Expressions für diese Art von Arbeit sind groß.

Verwandte Themen