Ich verwende einen rekursiven CTE für eine Baumstruktur, um alle Nachkommen eines bestimmten Knotens im Baum aufzulisten. Wenn ich einen Literalknotenwert in meine WHERE
Klausel schreibe , scheint SQL Server den CTE tatsächlich nur auf diesen Wert anzuwenden, wodurch ein Abfrageplan mit geringen tatsächlichen Zeilenzahlen usw. erstellt wird :
Wenn ich jedoch den Wert als Parameter übergebe, scheint er den CTE zu realisieren (zu spulen) und ihn dann nachträglich zu filtern :
Ich könnte die Pläne falsch lesen. Ich habe kein Leistungsproblem bemerkt, befürchte jedoch, dass die Realisierung des CTE Probleme mit größeren Datenmengen verursachen könnte, insbesondere in einem geschäftigeren System. Außerdem addiere ich diese Durchquerung normalerweise auf sich selbst: Ich gehe zu Vorfahren und zurück zu Nachkommen (um sicherzustellen, dass ich alle zugehörigen Knoten sammle). Aufgrund meiner Daten ist jeder Satz „verwandter“ Knoten eher klein, sodass die Realisierung des CTE keinen Sinn ergibt. Und wenn SQL Server den CTE zu erkennen scheint, gibt es mir einige ziemlich große Zahlen in seinen "tatsächlichen" Zählungen.
Gibt es eine Möglichkeit, die parametrisierte Version der Abfrage so zu gestalten, dass sie sich wie die Literalversion verhält? Ich möchte den CTE wiederverwendbar machen.
Abfrage mit Literal:
CREATE PROCEDURE #c AS BEGIN;
WITH descendants AS (SELECT
t.ParentId Id
,t.Id DescendantId
FROM #tree t
WHERE t.ParentId IS NOT NULL
UNION ALL SELECT
d.Id
,t.Id DescendantId
FROM descendants d
JOIN #tree t ON d.DescendantId = t.ParentId)
SELECT d.*
FROM descendants d
WHERE d.Id = 24
ORDER BY d.Id, d.DescendantId;
END;
GO
EXEC #c;
Abfrage mit Parameter:
CREATE PROCEDURE #c (@Id BIGINT) AS BEGIN;
WITH descendants AS (SELECT
t.ParentId Id
,t.Id DescendantId
FROM #tree t
WHERE t.ParentId IS NOT NULL
UNION ALL SELECT
d.Id
,t.Id DescendantId
FROM descendants d
JOIN #tree t ON d.DescendantId = t.ParentId)
SELECT d.*
FROM descendants d
WHERE d.Id = @Id
ORDER BY d.Id, d.DescendantId;
END;
GO
EXEC #c 24;
Setup-Code:
DECLARE @count BIGINT = 100000;
CREATE TABLE #tree (
Id BIGINT NOT NULL PRIMARY KEY
,ParentId BIGINT
);
CREATE INDEX tree_23lk4j23lk4j ON #tree (ParentId);
WITH number AS (SELECT
CAST(1 AS BIGINT) Value
UNION ALL SELECT
n.Value * 2 + 1
FROM number n
WHERE n.Value * 2 + 1 <= @count
UNION ALL SELECT
n.Value * 2
FROM number n
WHERE n.Value * 2 <= @count)
INSERT #tree (Id, ParentId)
SELECT n.Value, CASE WHEN n.Value % 3 = 0 THEN n.Value / 4 END
FROM number n;