Sto usando un CTE ricorsivo su una struttura ad albero per elencare tutti i discendenti di un particolare nodo nella struttura. Se scrivo un valore letterale di nodo nella mia WHERE
clausola, SQL Server sembra effettivamente applicare il CTE proprio a quel valore, fornendo un piano di query con conteggi di righe effettivi bassi, eccetera :
Tuttavia, se passo il valore come parametro, sembra realizzare (spool) il CTE e quindi filtrarlo dopo il fatto :
Potrei leggere male i piani. Non ho notato un problema di prestazioni, ma sono preoccupato che la realizzazione del CTE possa causare problemi con set di dati più grandi, specialmente in un sistema più occupato. Inoltre, normalmente compongo questo attraversamento su se stesso: attraverso fino agli antenati e torno ai discendenti (per assicurarmi di raccogliere tutti i nodi correlati). A causa di come sono i miei dati, ogni set di nodi "correlati" è piuttosto piccolo, quindi la realizzazione del CTE non ha senso. E quando SQL Server sembra realizzare il CTE, mi sta dando alcuni numeri abbastanza grandi nei suoi conteggi "reali".
C'è un modo per far sì che la versione parametrizzata della query si comporti come la versione letterale? Voglio mettere il CTE in una vista riutilizzabile.
Query con letterale:
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;
Query con parametro:
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;
Codice di installazione:
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;