Eu tenho uma consulta em que o uso select *
não apenas faz muito menos leituras, mas também usa significativamente menos tempo de CPU do que o uso select c.Foo
.
Esta é a consulta:
select top 1000 c.ID
from ATable a
join BTable b on b.OrderKey = a.OrderKey and b.ClientId = a.ClientId
join CTable c on c.OrderId = b.OrderId and c.ShipKey = a.ShipKey
where (a.NextAnalysisDate is null or a.NextAnalysisDate < @dateCutOff)
and b.IsVoided = 0
and c.ComplianceStatus in (3, 5)
and c.ShipmentStatus in (1, 5, 6)
order by a.LastAnalyzedDate
Isso terminou com 2.473.658 leituras lógicas, principalmente na Tabela B. Usou 26.562 CPU e teve uma duração de 7.965.
Este é o plano de consulta gerado:
No PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Quando mudo c.ID
para *
, a consulta terminou com 107.049 leituras lógicas, distribuídas uniformemente entre as três tabelas. Usou 4.266 CPU e teve uma duração de 1.147.
Este é o plano de consulta gerado:
No PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
Tentei usar as dicas de consulta sugeridas por Joe Obbish, com os seguintes resultados:
select c.ID
sem dica: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
com dica: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select *
sem dica: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
com dica: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
O uso da OPTION(LOOP JOIN)
dica com select c.ID
reduziu drasticamente o número de leituras em comparação com a versão sem a dica, mas ainda está fazendo cerca de 4x o número de leituras da select *
consulta sem nenhuma dica. A adição OPTION(RECOMPILE, HASH JOIN)
à select *
consulta fez com que o desempenho fosse muito pior do que qualquer outra coisa que tentei.
Após atualizar as estatísticas nas tabelas e nos índices usando WITH FULLSCAN
, a select c.ID
consulta está executando muito mais rapidamente:
select c.ID
antes da atualização: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select *
antes da atualização: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID
após a atualização: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
após a atualização: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
ainda supera select c.ID
em termos de duração total e total de leituras ( select *
tem cerca de metade das leituras), mas usa mais CPU. No geral, eles estão muito mais próximos do que antes da atualização, no entanto, os planos ainda diferem.
O mesmo comportamento é observado em 2016, em execução no modo de compatibilidade de 2014 e em 2014. O que poderia explicar a disparidade entre os dois planos? Será que os índices "corretos" não foram criados? As estatísticas poderiam estar um pouco desatualizadas?
Tentei mover os predicados para a ON
parte da junção, de várias maneiras, mas o plano de consulta é o mesmo a cada vez.
Após a recriação do índice
Eu reconstruí todos os índices nas três tabelas envolvidas na consulta. c.ID
ainda está fazendo o máximo de leituras (mais do dobro *
), mas o uso da CPU é cerca da metade da *
versão. A c.ID
versão também derramado tempdb na classificação de ATable
:
c.ID
: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
: https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
Também tentei forçá-lo a operar sem paralelismo, e isso me deu a consulta com melhor desempenho: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Percebo a contagem de execução dos operadores APÓS o grande índice procurar que está executando a ordenação executada apenas 1.000 vezes na versão single-threaded, mas fez significativamente mais na versão paralela, entre 2.622 e 4.315 execuções de vários operadores.
select c.ID
consulta muito mais rápida, mas ainda está fazendo algum trabalho extra que aselect *
consulta, sem dicas, faz.Estatísticas obsoletas certamente podem fazer com que o otimizador escolha um método ruim de localizar os dados. Você já tentou fazer um
UPDATE STATISTICS ... WITH FULLSCAN
ou um totalREBUILD
no índice? Tente isso e veja se ajuda.ATUALIZAR
De acordo com uma atualização do OP:
Portanto, agora, se a única ação tomada foi
UPDATE STATISTICS
, tente executar um índiceREBUILD
(nãoREORGANIZE
), como já vi, que ajuda na contagem estimada de linhas, onde ambosUPDATE STATISTICS
e o índiceREORGANIZE
não.fonte
a) reescreva cada tabela como uma subconsulta, seguindo estas regras:
b) SELECT - coloque primeiro as colunas de junção
c) PREDICATES - mude para as respectivas subconsultas
d) ORDER BY - mude para a subconsultas respectivas, classifique em JOIN COLUMNS FIRST
e) Adicione uma consulta de wrapper para sua classificação final e SELECT.
A idéia é pré-classificar as colunas de junção dentro de cada subseleção, colocando as colunas de junção primeiro em cada lista de seleção.
Aqui está o que eu quero dizer ....
fonte
ORDER BY
é inválido em uma subconsulta sem TOP, FORXML, etc. Tentei sem asORDER BY
cláusulas, mas era o mesmo plano.