J'ai une requête dans laquelle l'utilisation select *
non seulement fait beaucoup moins de lectures, mais utilise également beaucoup moins de temps processeur que l'utilisation select c.Foo
.
Voici la requête:
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
Cela s'est terminé avec 2 473 658 lectures logiques, principalement dans le tableau B. Il a utilisé 26 562 CPU et avait une durée de 7 965.
Voici le plan de requête généré:
Sur PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Lorsque je passe c.ID
à *
, la requête s'est terminée avec 107 049 lectures logiques, réparties assez uniformément entre les trois tables. Il utilisait 4 266 CPU et avait une durée de 1 147.
Voici le plan de requête généré:
Sur PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
J'ai tenté d'utiliser les indices de requête suggérés par Joe Obbish, avec ces résultats:
select c.ID
sans indice: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID
avec indice: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select *
sans indice: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select *
avec indice: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
L'utilisation de l' OPTION(LOOP JOIN)
indice avec select c.ID
a considérablement réduit le nombre de lectures par rapport à la version sans indice, mais il fait toujours environ 4x le nombre de lectures de la select *
requête sans aucun indice. L'ajout OPTION(RECOMPILE, HASH JOIN)
à la select *
requête l'a rendu bien pire que tout ce que j'ai essayé.
Après la mise à jour des statistiques sur les tables et leurs index à l'aide WITH FULLSCAN
, la select c.ID
requête s'exécute beaucoup plus rapidement:
select c.ID
avant la mise à jour: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select *
avant la mise à jour: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID
après la mise à jour: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select *
après la mise à jour: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *
surpasse toujours select c.ID
en termes de durée totale et de lectures totales ( select *
a environ la moitié des lectures) mais il utilise plus de CPU. Dans l'ensemble, ils sont beaucoup plus proches qu'avant la mise à jour, mais les plans diffèrent toujours.
Le même comportement est observé sur 2016 en cours d'exécution en 2014 Mode de compatibilité et sur 2014. Qu'est-ce qui pourrait expliquer la disparité entre les deux plans? Se pourrait-il que les index "corrects" n'aient pas été créés? Des statistiques légèrement dépassées peuvent-elles en être la cause?
J'ai essayé de déplacer les prédicats vers la ON
partie de la jointure, de plusieurs manières, mais le plan de requête est le même à chaque fois.
Après la reconstruction d'index
J'ai reconstruit tous les index des trois tables impliquées dans la requête. c.ID
fait toujours le plus de lectures (plus de deux fois plus *
), mais l'utilisation du processeur représente environ la moitié de la *
version. La c.ID
aussi la version déversée dans tempdb sur le tri des ATable
:
c.ID
: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
*
: https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
J'ai également essayé de le forcer à fonctionner sans parallélisme, ce qui m'a donné la requête la plus performante: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Je remarque le nombre d'exécutions d'opérateurs APRÈS la recherche d'index volumineux qui effectue l'ordre seulement 1000 fois dans la version à un seul thread, mais a fait beaucoup plus dans la version parallélisée, entre 2622 et 4315 exécutions de divers opérateurs.
select c.ID
requête beaucoup plus rapide, mais il fait toujours un travail supplémentaire que laselect *
requête, sans conseils, fait.