Por que essa consulta não usa um spool de índice?

23

Estou fazendo essa pergunta para entender melhor o comportamento do otimizador e entender os limites dos spools de índice. Suponha que eu coloque números inteiros de 1 a 10000 em um heap:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

E forçar uma junção aninhada com MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Esta é uma ação bastante hostil a ser tomada em relação ao SQL Server. Junções de loop aninhadas geralmente não são uma boa opção quando ambas as tabelas não possuem índices relevantes. Aqui está o plano:

consulta incorreta

A consulta leva 13 segundos na minha máquina com 100000000 linhas buscadas no spool da tabela. No entanto, não vejo por que a consulta precisa ser lenta. O otimizador de consulta tem a capacidade de criar índices dinamicamente através de spools de índice . Essa consulta parece ser uma candidata perfeita para um spool de índice.

A consulta a seguir retorna os mesmos resultados que a primeira, possui um spool de índice e termina em menos de um segundo:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

solução alternativa 1

Essa consulta também possui um spool de índice e termina em menos de um segundo:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

solução alternativa 2

Por que a consulta original não possui um spool de índice? Existe algum conjunto de dicas ou sinalizadores de rastreamento documentados ou não documentados que fornecerão um spool de índice? Encontrei essa pergunta relacionada , mas ela não responde totalmente à minha pergunta e não consigo que o sinalizador de rastreamento misterioso funcione para esta consulta.

Joe Obbish
fonte

Respostas:

20

Como você sabe, a pesquisa do otimizador não é exaustiva. Ele tenta coisas que fazem sentido no contexto e que freqüentemente pagam dividendos em consultas reais. Forçar uma junção de loop entre duas tabelas de heap não indexadas de coluna única não é esse cenário. Dito isto, aqui estão alguns detalhes:

O SQL Server gosta de transformar se aplica a junções mais cedo, porque conhece mais truques com junções. Posteriormente, ele poderá explorar a conversão da junção novamente em um aplicativo. A diferença entre os dois sendo parâmetros correlacionados (referências externas). Aplica-se faz sentido quando há um índice adequado no lado interno. Seu exemplo não possui índices, portanto, o otimizador não é convencido a explorar a tradução para um aplicativo.

Uma junção simples (não aplicável) possui o predicado de junção no operador de junção, em vez de referências externas. A otimização do spool para uma não aplicação geralmente é um spool de tabela lento, pois não há predicado no lado interno, apenas na junção.

O otimizador não considera criar um índice rapidamente para permitir uma aplicação; em vez disso, a sequência de eventos geralmente é o inverso: transformar para aplicar porque existe um bom índice.

Às vezes, você pode incentivar uma aplicação em vez de uma associação usando a APPLYsintaxe em sua consulta. O sinalizador de rastreio não documentado 9114 pode ajudar nisso, dissuadindo o otimizador de converter um aplicar lógico para uma junção inicial. Por exemplo:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Plano de spool

Um spool de índice é preferido para aplicação, porque a referência externa significa que a seleção é aplicada no lado interno da união. Você verá isso frequentemente, SelToIndexOnTheFlymas existem outros caminhos. Veja meu artigo The Eager Index Spool e The Optimizer .

Paul White diz que a GoFundMonica
fonte