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.IDsans 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.IDa 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.IDrequête s'exécute beaucoup plus rapidement:
select c.IDavant 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.IDen 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 ONpartie 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.IDfait 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.IDaussi 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.IDrequête beaucoup plus rapide, mais il fait toujours un travail supplémentaire que laselect *requête, sans conseils, fait.