Tenho uma tabela com estrutura em árvore (definida por hierarchyid
coluna) e quero selecionar todos os descendentes de um determinado registro. Para isso, estou usando hiearchyid.IsDescendantOf()
o método.
Eu esperava que, como não estou fazendo comparações simples, mas estou executando operações (neste caso, estou chamando o IsDescendantOf()
método), obterei um plano de execução terrível com varreduras de índice e outros enfeites.
Mesmo assim, o SQL Server o otimizou para uma pequena busca de índice.
Estou intrigado por que e como.
A chamada de métodos em tipos CLR geralmente é otimizada? Presumi que o SQL Server vê os tipos CLR como uma caixa preta opaca e, portanto, não pode fazer sua mágica funcionar nele. (Uma vez que também não é possível fazer isso em funções SQL nativas.)
Ou isso é apenas para este método específico? (Como os hieararchyid
valores são ordenados em profundidade, eu poderia obter resultados semelhantes apenas fazendo comparações.)
Demonstração:
CREATE TABLE dbo.HierarchyExample (
Id INT PRIMARY KEY,
Hieararchy HIERARCHYID NOT NULL
);
INSERT INTO dbo.HierarchyExample(Id, Hieararchy)
VALUES
(1, hierarchyid::Parse('/1/')),
(2, hierarchyid::Parse('/1/1/')),
(3, hierarchyid::Parse('/1/2/')),
(4, hierarchyid::Parse('/1/3/')),
(5, hierarchyid::Parse('/1/3/1/')),
(6, hierarchyid::Parse('/1/3/2/')),
(7, hierarchyid::Parse('/1/3/3/')),
(8, hierarchyid::Parse('/1/4/')),
(9, hierarchyid::Parse('/1/4/1/')),
(10, hierarchyid::Parse('/1/4/2/'));
CREATE INDEX IX_HierarchyExample_Hierarchy
ON dbo.HierarchyExample (Hieararchy);
SELECT descendant.*
FROM HierarchyExample ancestor
INNER JOIN HierarchyExample descendant
ON descendant.Hieararchy.IsDescendantOf(ancestor.Hieararchy) = 1
WHERE ancestor.Id = 1
DROP TABLE IF EXISTS dbo.HierarchyExample;
descendant.Hieararchy.IsDescendantOf(ancestor.Hieararchy) = 1
certamente não parece que deveria ser sargável, mas parece fazer algumas travessuras logo no início do processo para este caso específico .Se eu tentar
O plano de execução resultante mostra uma busca
A expressão de intervalo já está presente na árvore convertida .
Portanto, concluo que durante a análise isso é convertido em um predicado de intervalo potencialmente pesquisável.
Essa transformação é mencionada de passagem no artigo Suporte relacional para cenários de esquema flexível, embora não entre em grandes detalhes.
Depois disso, a otimização acontece normalmente. As regras de transformação de consulta usadas depois disso são apenas
SelIdxToRng
eSelToTrivialFilter
- nada específico para HierarchyId.A árvore convertida para seu exemplo de junção mais complicado está abaixo, mostrando praticamente a mesma coisa