Estou usando um CTE recursivo em uma estrutura em árvore para listar todos os descendentes de um nó específico na árvore. Se eu escrever um valor nó literal no meu WHERE
cláusula, o SQL Server parece realmente aplicar o CTE apenas a esse valor, dando um plano de consulta com baixas contagens linhas reais, et cetera :
No entanto, se eu passar o valor como parâmetro, ele parece realizar (spool) o CTE e filtrá-lo após o fato :
Eu poderia estar lendo os planos errado. Não notei um problema de desempenho, mas estou preocupado que a realização do CTE possa causar problemas com conjuntos de dados maiores, especialmente em um sistema mais ocupado. Além disso, eu normalmente componho essa travessia em si mesma: vou até os ancestrais e volto aos descendentes (para garantir que reuno todos os nós relacionados). Devido à forma como meus dados são, cada conjunto de nós "relacionados" é bastante pequeno, portanto a realização do CTE não faz sentido. E quando o SQL Server parece realizar o CTE, ele está me dando alguns números bastante grandes em suas contagens "reais".
Existe uma maneira de obter a versão parametrizada da consulta para agir como a versão literal? Quero colocar o CTE em uma exibição reutilizável.
Consulta com 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 com o 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 instalação:
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;