Eu tenho uma mesa com algumas dezenas de linhas. A configuração simplificada está seguindo
CREATE TABLE #data ([Id] int, [Status] int);
INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);
E eu tenho uma consulta que une esta tabela a um conjunto de linhas construídas com valor de tabela (feitas de variáveis e constantes), como
DECLARE @id1 int = 101, @id2 int = 105;
SELECT
COALESCE(p.[Code], 'X') AS [Code],
COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
(@id1, 'A'),
(@id2, 'B')
) p([Id], [Code])
FULL JOIN #data d ON d.[Id] = p.[Id];
O plano de execução da consulta está mostrando que a decisão do otimizador é usar a FULL LOOP JOIN
estratégia, o que parece apropriado, pois as duas entradas têm muito poucas linhas. Uma coisa que notei (e não posso concordar), porém, é que as linhas do TVC estão sendo colocadas em spool (consulte a área do plano de execução na caixa vermelha).
Por que o otimizador apresenta o spool aqui, qual é o motivo para fazê-lo? Não há nada complexo além do carretel. Parece que não é necessário. Como se livrar dele, neste caso, quais são as formas possíveis?
O plano acima foi obtido em
Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)
fonte
Respostas:
O que está além do spool não é uma referência simples de tabela, que pode ser simplesmente duplicada quando a junção esquerda / anti-junção esquerda alternativa de for gerada.
Pode parecer um pouco com uma tabela (Constant Scan), mas para o otimizador *, é uma
UNION ALL
das linhas separadas naVALUES
cláusula.A complexidade adicional é suficiente para o otimizador optar por colocar em spool e repetir as linhas de origem, e não substituir o spool por um simples "table get" mais tarde. Por exemplo, a transformação inicial da junção completa é assim:
Observe os carretéis extras introduzidos pela transformação geral. Carretéis acima de uma tabela simples são limpos posteriormente pela regra
SpoolGetToGet
.Se o otimizador tivesse uma
SpoolConstGetToConstGet
regra correspondente , ele poderia funcionar como você deseja, em princípio.Use uma tabela real (temporária ou variável) ou grave a transformação manualmente da junção completa, por exemplo:
Planeje a reescrita manual:
Isso tem um custo estimado de 0,0067201 unidades, em comparação com 0,0203412 unidades para o original.
* Pode ser observado como
LogOp_UnionAll
na Árvore Convertida (TF 8605). Na Árvore de Entrada (TF 8606), é aLogOp_ConstTableGet
. A Árvore Convertida mostra a árvore dos elementos de expressão do otimizador após análise, normalização, algebrização, ligação e algum outro trabalho preparatório. A Árvore de Entrada mostra os elementos após a conversão para o Negation Normal Form (NNF convert), o colapso constante do tempo de execução e alguns outros bits e bobs. A conversão NNF inclui lógica para recolher uniões lógicas e tabelas comuns, entre outras coisas.fonte
O spool de tabela é simplesmente criar uma tabela dos dois conjuntos de tuplas presentes no
VALUES
cláusula.Você pode eliminar o spool inserindo esses valores em uma tabela temporária primeiro, da seguinte maneira:
Observando o plano de execução da sua consulta, vemos que a lista de saída contém duas colunas que usam o
Union
prefixo; esta é uma dica de que o spool está criando uma tabela a partir de uma fonte da união:o
FULL OUTER JOIN
exige que o SQL Server acesse os valoresp
duas vezes, uma vez para cada "lado" da associação. Criar um spool permite que os loops internos resultantes se juntem para acessar os dados em spool.Curiosamente, se você substituir o
FULL OUTER JOIN
por umLEFT JOIN
e umRIGHT JOIN
eUNION
os resultados juntos, o SQL Server não usará um spool.Observe que não estou sugerindo o uso da
UNION
consulta acima; para conjuntos maiores de entrada, pode não ser mais eficiente do que o simples queFULL OUTER JOIN
você já possui.fonte