Estoy usando un CTE recursivo en una estructura de árbol para enumerar todos los descendientes de un nodo particular en el árbol. Si escribo un valor de nodo literal en mi WHERE
cláusula, SQL Server parece aplicarse en realidad el CTE sólo para ese valor, dando un plan de consulta con bajos recuentos de filas reales, etcétera :
Sin embargo, si paso el valor como parámetro, parece darse cuenta ( poner en cola) el CTE y luego filtrarlo después del hecho :
Podría estar leyendo mal los planes. No he notado un problema de rendimiento, pero me preocupa que la realización del CTE pueda causar problemas con conjuntos de datos más grandes, especialmente en un sistema más ocupado. Además, normalmente compongo este recorrido en sí mismo: atravieso a los antepasados y retrocedo a los descendientes (para asegurarme de reunir todos los nodos relacionados). Debido a cómo son mis datos, cada conjunto de nodos "relacionados" es bastante pequeño, por lo que la realización del CTE no tiene sentido. Y cuando SQL Server parece darse cuenta del CTE, me está dando algunos números bastante grandes en sus recuentos "reales".
¿Hay alguna manera de hacer que la versión parametrizada de la consulta actúe como la versión literal? Quiero poner el CTE en una vista reutilizable.
Consulta con 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;
Consulta con parámetro:
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;
Código de configuración:
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;