2017-06-23 7 views
2

Ich arbeite in SQL Server 2014. Ich habe eine seltsame Datenhierarchie Situation. (Zumindest habe ich so etwas noch nie erlebt.)SQL komplizierte rekursive CTE

In meiner Hierarchie gibt es mehrere Root/High-Level-Objekte. Jedes Objekt unterhalb der Stammebene wird einem einzigen Objekt darüber zugeordnet. Nicht jeder Knotenpfad hat die gleiche Länge. Zum Beispiel könnte ein Pfad 2 Objektebenen enthalten, während ein anderer Pfad 20 Objektebenen enthalten könnte.

Jedes Objekt in der Hierarchie hat ein IsInherited Attribut, sowie einige andere Attribute (nennen Sie es SomeAttribute). Das Attribut IsInherited gibt an, ob das angegebene Objekt den Wert SomeAttribute von seinem unmittelbaren übergeordneten Element erbt. Wenn das IsInherited Attribut eines Objekts 'Y' ist, dann erbt das gegebene Objekt natürlich den Wert SomeAttribute von seinem unmittelbarsten Elternelement (das wiederum seinen Wert für sein unmittelbarstes Elternelement usw. erben könnte). Andernfalls wird der Wert SomeAttribute des Objekts angegeben.

Nun ist das Ganze nicht unbedingt ungewöhnlich. Was diese Situation unüblich macht, ist die spezielle Implementierung dieser Vererbung. Diese Hierarchie wird in einer einzigen Tabelle gespeichert (macht dies dies zu einem Adjazenzlistenmodell?), Und der Wert SomeAttribute ist nicht für das angegebene Objekt/die Zeile gefüllt, wenn sein IsInherited Attribut 'Y' ist.

Mein Ziel ist es, den Wert SomeAttribute für alle Objekte in der Hierarchie zurückzugeben.

Eine Probe der Tabelle ist wie folgt:

CREATE TABLE hierarchy (
    ID int NOT NULL 
    ,ParentID int NULL 
    ,SomeAttribute char(1) NULL 
    ,IsInherited char(1) NOT NULL 
) 
; 
INSERT INTO hierarchy (ID, ParentID, SomeAttribute, IsInherited) 
VALUES 
(1, NULL, 'a', 'N') 
,(2, NULL, 'b', 'N') 
,(3, NULL, 'c', 'N') 
,(4, NULL, 'd', 'N') 
,(5, NULL, 'e', 'N') 
,(6, NULL, 'f', 'N') 
,(7, NULL, 'g', 'N') 
,(8, 2, NULL, 'Y') 
,(9, 3, 'h', 'N') 
,(10, 4, NULL, 'Y') 
,(11, 5, 'j', 'N') 
,(12, 6, NULL, 'Y') 
,(13, 6, 'k', 'N') 
,(14, 7, 'l', 'N') 
,(15, 7, 'm', 'N') 
,(16, 10, NULL, 'Y') 
,(17, 11, NULL, 'Y') 
,(18, 16, NULL, 'Y') 
,(19, 17, NULL, 'Y') 
,(20, 19, 'o', 'N') 
; 

Dies gibt uns die folgenden Knotenpfade:

1 
2-8 
3-9 
4-10-16-18 
5-11-17-19-20 
6-12,13 
7-14,15 

Also, mit diesem Probentisch, erwarte ich zurück:

ID SomeAttribute 
1  a 
2  b 
3  c 
4  d 
5  e 
6  f 
7  g 
8  b (inherited from 2) 
9  h 
10 d (inherited from 4) 
11 j 
12 f (inherited from 6) 
13 k 
14 l 
15 m 
16 d (inherited from 10, inherited from 4) 
17 j (inherited from 11) 
18 d (inherited from 16, inherited from 10, inherited from 4) 
19 j (inherited from 17, inherited from 11) 
20 o 

Ich weiß, dass dies wahrscheinlich rekursive CTEs erfordert. Ich kämpfe, um das SQL dafür zu schreiben. Wie gebe ich die gewünschte Ausgabe zurück?

Antwort

4

Dies ist das Adjazenzlistenmodell, da jede Zeile ein Paar benachbarter Knoten darstellt.

Und eine rekursive CTE würde wie folgt aussehen:

with q as 
(
    select id, SomeAttribute, cast(id as varchar(max)) SomeAttributePath 
    from hierarchy 
    where ParentID is null 
    union all 
    select c.id, case when c.IsInherited = 'Y' then q.SomeAttribute else c.SomeAttribute end as SomeAttribute, cast(concat(q.SomeAttributePath,'-',c.id) as varchar(max)) 
    from q 
    join hierarchy c 
    on c.ParentID = q.ID 
) 
select * 
from q 
order by id 

outputing:

id   SomeAttribute SomeAttributePath 
----------- ------------- ----------------- 
1   a    1 
2   b    2 
3   c    3 
4   d    4 
5   e    5 
6   f    6 
7   g    7 
8   b    2-8 
9   h    3-9 
10   d    4-10 
11   j    5-11 
12   f    6-12 
13   k    6-13 
14   l    7-14 
15   m    7-15 
16   d    4-10-16 
17   j    5-11-17 
18   d    4-10-16-18 
19   j    5-11-17-19 
20   o    5-11-17-19-20 
+1

Ich hatte eine Besetzung um Ihre concat zu setzen, um die Abfrage laufen zu lassen. Noch wichtiger ist jedoch, dass einige der SomeAttribute-Werte falsch sind. Zum Beispiel sollte 20 o sein, nicht e, weil sein Flag IsInherited N ist; In ähnlicher Weise sollte 9 h sein. – user3100444

+0

@ user3100444 Danke. Ich habe deine Änderung versehentlich abgelehnt und die Änderung manuell übernommen. –

+1

Wenn Sie den Pfad für die Attribute anzeigen möchten, der auf den tatsächlichen Pfad beschränkt ist, kann auch für die dritte Spalte ein CASE-Ausdruck verwendet werden. 'FALL WENN C.IsInherited = 'Y' DANN q.SomeAttributePath + '-' ELSE '' END + CAST (c.id AS VARCHAR (MAX))' –