Como (e por que) o TOP afeta um plano de execução?

35

Para uma consulta moderadamente complexa que estou tentando otimizar, observei que a remoção da TOP ncláusula altera o plano de execução. Eu teria imaginado que, quando uma consulta inclui TOP no mecanismo de banco de dados, ela executa a consulta ignorando a TOPcláusula the e, no final, diminui o resultado definido para o número n de linhas solicitadas. O plano gráfico de execução parece indicar que este é o caso - TOPé o "último" passo. Mas parece que há mais coisas acontecendo.

Minha pergunta é: como (e por que) uma cláusula TOP n afeta o plano de execução de uma consulta?

Aqui está uma versão simplificada do que está acontecendo no meu caso:

A consulta está combinando linhas de duas tabelas, A e B.

Sem a TOPcláusula, o otimizador estima que haverá 19k linhas da tabela A e 46k linhas da tabela B. O número real de linhas retornadas é 16k para A e 13k para B. Uma correspondência de hash é usada para unir esses dois conjuntos de resultados para um total de 69 linhas (uma classificação é aplicada). Essa consulta acontece muito rapidamente.

Quando adiciono, TOP 1001o otimizador não usa uma combinação de hash; em vez disso, classifica primeiro os resultados da tabela A (mesma estimativa / real de 19k / 16k) e executa um loop aninhado na tabela B. O número estimado de linhas da tabela B agora é 1, e o estranho é que isso TOP nafeta diretamente o número estimado de execuções (busca de índice) contra B - parece sempre ser 2n + 1 , ou, no meu caso, 2003. Essa estimativa muda de acordo se eu mudar TOP n. Obviamente, como essa é uma junção aninhada, o número real de execuções é 16k (o número de linhas da tabela A) e isso torna a consulta mais lenta.

O cenário atual é um pouco mais complexo, mas isso captura a ideia / comportamento básico. Ambas as tabelas são pesquisadas usando pesquisas de índice. Esta é a edição do SQL Server 2008 R2 Enterprise.

David
fonte
A consulta possui uma ORDER BYcláusula Adicionando TOPalterações onde esse tipo de plano ocorre, mas estou mais preocupado com a forma como isso afeta o número de execuções de buscas de índice na tabela B ... (é claro que as duas podem estar relacionadas - não sei)
David
11
Discussão relacionada: a FAST num_rowsdica de consulta.
Remus Rusanu

Respostas:

39

Eu imaginaria que, quando uma consulta incluir TOP n, o mecanismo de banco de dados executaria a consulta ignorando a cláusula TOP e, no final, reduziria apenas o resultado definido para o número n de linhas solicitadas. O plano gráfico de execução parece indicar que este é o caso - TOP é o "último" passo. Mas parece que há mais coisas acontecendo.

A maneira como o texto acima é formulado me faz pensar que você pode ter uma imagem mental incorreta de como uma consulta é executada. Um operador em um plano de consulta não é uma etapa (onde o conjunto de resultados completo de uma etapa anterior é avaliado pela próxima.

O SQL Server usa um modelo de execução em pipeline , em que cada operador expõe métodos como Init () , GetRow () e Close () . Como o nome GetRow () sugere, um operador produz uma linha por vez, sob demanda (conforme exigido pelo operador pai). Isso está documentado na referência de Operadores lógicos e físicos dos Manuais Online , com mais detalhes em minha postagem no blog Por que os planos de consulta são executados para trás . Esse modelo de linha por vez é essencial para formar uma intuição sólida para a execução de consultas.

Minha pergunta é: como (e por que) uma TOPcláusula n afeta o plano de execução de uma consulta?

Algumas operações lógicas como TOPsemi-junções e a FAST n dica de consulta afetam a maneira como o otimizador de consulta custa alternativas ao plano de execução. A idéia básica é que uma possível forma de plano possa retornar as primeiras n linhas mais rapidamente do que um plano diferente que foi otimizado para retornar todas as linhas.

Por exemplo, a junção de loops aninhados indexados geralmente é a maneira mais rápida de retornar um pequeno número de linhas, embora a junção de hash ou mesclagem com varreduras possa ser mais eficiente em conjuntos maiores. A maneira como o otimizador de consulta raciocina sobre essas opções é definindo uma meta de linha em um ponto específico na árvore lógica de operações.

Uma meta de linha modifica a maneira como as alternativas do plano de consulta são avaliadas. A essência disso é que o otimizador começa com o custo de cada operador como se o conjunto de resultados completo fosse necessário, define uma meta de linha no ponto apropriado e depois trabalha de volta na árvore do plano, estimando o número de linhas que ele espera examinar para atingir a meta da linha.

Por exemplo, uma lógica TOP(10)define uma meta de linha 10 em um ponto específico da árvore de consultas lógicas. Os custos dos operadores que antecedem o objetivo da linha são modificados para estimar quantas linhas eles precisam produzir para atingir o objetivo da linha. Esse cálculo pode se tornar complexo, portanto, é mais fácil entender tudo isso com um exemplo completo e planos de execução anotados. As metas de linha podem afetar mais do que a escolha do tipo de junção ou se as buscas e pesquisas são preferidas às verificações. Mais detalhes sobre isso aqui .

Como sempre, um plano de execução selecionado com base em uma meta de linha está sujeito às habilidades de raciocínio do otimizador e à qualidade das informações fornecidas a ele. Nem todo plano com uma meta de linha produzirá o número necessário de linhas mais rapidamente na prática, mas de acordo com o modelo de custo que produzirá.

Nos casos em que um plano de meta de linha não é mais rápido, geralmente há maneiras de modificar a consulta ou fornecer melhores informações ao otimizador, de modo que o plano naturalmente selecionado seja o melhor. Qual opção é apropriada no seu caso depende dos detalhes do curso. O recurso de meta de linha geralmente é muito eficaz (embora exista um bug a ser observado quando usado em planos de execução paralelos).

Sua consulta e plano específicos podem não ser adequados para uma análise detalhada aqui (por todos os meios, forneça um plano de execução real, se você desejar), mas espero que as idéias descritas aqui permitam que você avance.

Paul White diz que a GoFundMonica
fonte
12

Quando você usa o TOP, o Optimizer vê uma oportunidade de fazer menos trabalho. Se você pedir 10 linhas, há uma boa chance de que ele não precise consumir todo o conjunto. Assim, o operador TOP pode ser empurrado muito mais para a direita. Ele continuará solicitando linhas do próximo operador (à direita) até receber o suficiente.

Você ressalta que, sem TOP, a consulta classifica os dados no final. Se o mecanismo puder saber quantas linhas serão satisfeitas pela junção com antecedência, é possível optar por usar um plano semelhante, posicionando o TOP à esquerda. Porém, com o esforço para fazer uma correspondência de hash ser relativamente alto e, presumivelmente, nenhuma opção para uma junção de mesclagem, o Otimizador pode preferir filtrar o TOP mais para a direita.

Quando a tabela B é consultada, ela está buscando uma única linha de cada vez. É por isso que a estimativa é 1. Ele também pressupõe que encontrará essa linha apenas 50% das vezes. Então, ele acha que precisará de 2n + 1 para encontrá-lo.

Rob Farley
fonte
Não parece certo que o número estimado de linhas seja alterado com base na maneira como os dados estão sendo buscados. Como obtém os dados não deve afetar a cardinalidade. Uma mudança na maneira como ele busca seria refletida no número de execuções, correto?
David
O "número estimado de linhas" é por execução. Em um loop aninhado, é bem provável que seja executado mais de uma vez.
precisa saber é o seguinte
Esse seria um comportamento diferente do número real de linhas e do número (real) de execuções. Se o plano real mostra 16.834 execuções reais e 15.407 linhas reais retornadas, entendo que isso fez 16k buscas, mas encontrou apenas 15k correspondentes ao predicado. Se isso significava 15k linhas por execução este seria 15k * 16k = 240 milhões de linhas - cerca de 10 vezes maior do que a mesa ...
David
Além disso, não sei se segui a última declaração de sua resposta. Quando você diz que 2n + 1 procura encontrar "it", o que você quer dizer com "it"? Certamente nem uma única linha? Você quer dizer que o otimizador assume que, para qualquer linha em A, há uma chance de 50% de corresponder a B e, portanto, será necessário "experimentar" as linhas de 2003 de A para obter 1001 correspondências de B? Esse comportamento está documentado em algum lugar pela Microsoft? E o que isso tem a ver com a TOPcláusula? Obrigado pela sua resposta / paciência.
David
Sim, as linhas estimadas são por execução. As linhas reais são totais. Embora não haja nenhum problema em um operador retornar mais linhas do que as da tabela, é muito fácil demonstrar os operadores retornando a mesma linha várias vezes.
Rob Farley