Estou usando um CTE recursivo em uma estrutura de árvore para listar todos os descendentes de um nó específico na árvore. Se eu escrever um valor de nó literal na minha WHERE
cláusula, o SQL Server parece realmente aplicar o CTE apenas a esse valor, fornecendo um plano de consulta com baixas contagens de linhas reais, etc .:
No entanto, se eu passar o valor como parâmetro, ele parece realizar (spool) o CTE e depois 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, normalmente componho essa travessia em si mesma: percorro os ancestrais e volto para os descendentes (para garantir que reuni todos os nós relacionados). Devido à forma como meus dados são, cada conjunto de nós “relacionados” é bastante pequeno, então a realização do CTE não faz sentido. E quando o SQL Server parece perceber o CTE, ele está me dando alguns números bastante grandes em suas contagens “reais”.
Existe uma maneira de fazer com que a versão parametrizada da consulta funcione como a versão literal? Eu quero colocar o CTE em uma visualizaçã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 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 configuraçã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;