J'utilise un CTE récursif sur une structure arborescente pour répertorier tous les descendants d'un nœud particulier dans l'arbre. Si j'écris une valeur de nœud littéral dans ma WHERE
clause, SQL Server semble appliquer le CTE uniquement à cette valeur, donnant un plan de requête avec un nombre de lignes réel faible, et cetera :
Cependant, si je passe la valeur en tant que paramètre, il semble réaliser (spouler) le CTE puis le filtrer après coup :
Je pourrais mal lire les plans. Je n'ai pas remarqué de problème de performances, mais je crains que la réalisation du CTE ne cause des problèmes avec des ensembles de données plus volumineux, en particulier dans un système plus chargé. De plus, je compose normalement cette traversée sur elle-même: je traverse jusqu'aux ancêtres et redescends aux descendants (pour m'assurer de rassembler tous les nœuds liés). En raison de la façon dont mes données sont, chaque ensemble de nœuds «liés» est plutôt petit, donc la réalisation du CTE n'a pas de sens. Et lorsque SQL Server semble réaliser le CTE, il me donne des chiffres assez importants dans ses décomptes «réels».
Existe-t-il un moyen pour que la version paramétrée de la requête agisse comme la version littérale? Je veux mettre le CTE dans une vue réutilisable.
Requête avec littéral:
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;
Requête avec paramètre:
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;
Code de configuration:
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;