Ich habe eine Tabelle, die eine Hierarchie definiert:SQL Query: Hierarchical Coalesce
Create Table [example] (
id Integer Not Null Primary Key,
parentID Integer Null,
largeData1 nVarChar(max) Null,
largeData2 nVarChar(max) Null);
-- largeData3...n also exist
Insert Into [example] (id, parentID, largeData1, largeData2)
Select 1, null, 'blah blah blah', null Union
Select 2, 1, null, null Union
Select 3, 1, 'foo bar foobar', null Union
Select 4, 3, null, 'lorem ipsum' Union
Select 5, 4, null, null;
Hierarchiediagramm für diese Daten:
Ich möchte schreiben, um eine Abfrage, die eine einzelne zurück Zeile für jeden gegebenen [ID] Wert. Die Zeile sollte die [id] und [parentID] Informationen dieser Zeile enthalten. Es sollte auch die Felder [largeData1 ... n] enthalten. Wenn ein largeData-Feld jedoch null ist, sollte es die Hierarchie durchlaufen, bis ein Nicht-Null-Wert für dieses Feld gefunden wird. Kurz gesagt sollte es wie die Coalesce-Funktion funktionieren, außer über eine Hierarchie von Zeilen statt einer Reihe von Spalten.
Beispiel:
Wo [id] = 1:
id: 1
parentID: null
largeData1: blah blah blah
largeData2: null
Wo [id] = 2
id: 1
parentID: 1
largeData1: blah blah blah
largeData2: null
Wo [id] = 3
id: 3
parentID: 1
largeData1: foo bar foobar
largeData2: null
Wo [id] = 4
id: 4
parentID: 3
largeData1: foo bar foobar
largeData2: lorem ipsum
Wo [id] = 5
id: 5
parentID: 4
largeData1: foo bar foobar
largeData2: lorem ipsum
Bisher habe ich diese:
Declare @id Integer; Set @id = 5;
With heirarchy
(id, parentID, largeData1, largeData2, [level])
As (
Select id, parentID, largeData1,
largeData2, 1 As [level]
From example
Where id = @id
Union All
Select parent.id, parent.parentID,
parent.largeData1,
parent.largeData2,
child.[level] + 1 As [level]
From example As parent
Inner Join heirarchy As child
On parent.id = child.parentID)
Select id, parentID,
(Select top 1 largeData1
From heirarchy
Where largeData1 Is Not Null
Order By [level] Asc) As largeData1,
(Select top 1 largeData2
From heirarchy
Where largeData2 Is Not Null
Order By [level] Asc) As largeData2
From example
Where [id] = @id;
Diese gibt die Ergebnisse zurück, nach denen ich suche. Gemäß dem Abfrageplan wird jedoch für jedes largeData-Feld, das ich zurückziehe, ein separater Durchlauf durch die Hierarchie durchgeführt.
Wie kann ich das effizienter machen?
Dies ist offensichtlich eine vereinfachte Version eines komplexeren Problems. Die letzte Abfrage gibt Daten im XML-Format zurück, sodass alle Lösungen, die die FOR XML-Klausel enthalten, vollkommen in Ordnung sind.
Ich kann eine CLR-Aggregatfunktion dafür erstellen, wenn dies helfen würde. Ich habe diese Route noch nicht erkundet.
+1 für das Hochdrücken von Nicht-Null-Werten. Aber die Verwendung von MAX kann problematisch sein. Wenn in Zeile 3 der Beispieldaten "acoo bar bar" anstelle von "foo bar bar" steht, gibt die Abfrage für @ id = 5 "blah blabla" für largeData1 zurück. – 8kb
Wenn der Wert einer Spalte bei einem gegebenen Wert null ist, wird er beim Ersetzen des CTE durch den Wert auf dieser Ebene ersetzt, andernfalls bleibt er unverändert. Eine Zeile wird pro Level produziert. Wenn die cte fertig ist, ist der Wert für eine Spalte über alle Zeilen entweder Null oder der erste gefundene Wert. Aggregationen ignorieren Nullen und lassen nur den einen Wert für max (oder min) übrig. –