У меня есть запрос, где использование select *не только делает гораздо меньше операций чтения, но также использует значительно меньше процессорного времени, чем использование select c.Foo.
Это запрос:
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
Это закончилось с 2473 658 логическими чтениями, главным образом в таблице B. Он использовал 26 562 CPU и имел продолжительность 7 965.
Это сгенерированный план запроса:
На PasteThePlan: https://www.brentozar.com/pastetheplan/?id=BJAp2mQIQ
Когда я переключаюсь c.IDна *, запрос завершается с 107 049 логическими чтениями, довольно равномерно распределенными между всеми тремя таблицами. Он использовал 4266 процессоров и имел продолжительность 1,147.
Это сгенерированный план запроса:
На PasteThePlan: https://www.brentozar.com/pastetheplan/?id=SyZYn7QUQ
Я попытался использовать подсказки запроса, предложенные Джо Оббишем, с такими результатами:
select c.IDбез подсказки: https://www.brentozar.com/pastetheplan/?id=SJfBdOELm
select c.ID с подсказкой: https://www.brentozar.com/pastetheplan/ ? id = B1W ___ N87
select * без подсказки: https://www.brentozar.com/pastetheplan/?id=HJ6qddEIm
select * с подсказкой: https://www.brentozar.com/pastetheplan/?id=rJhhudNIQ
Использование OPTION(LOOP JOIN)подсказки с select c.IDрадикально уменьшило количество операций чтения по сравнению с версией без подсказки, но оно все равно примерно в 4 раза превышает количество select *запросов на чтение без каких-либо подсказок. Добавление OPTION(RECOMPILE, HASH JOIN)к select *запросу сделало его работу намного хуже, чем все, что я пробовал.
После обновления статистики по таблицам и их индексов , используя WITH FULLSCAN, то select c.IDзапрос выполняется гораздо быстрее:
select c.IDперед обновлением: https://www.brentozar.com/pastetheplan/?id=SkiYoOEUm
select * перед обновлением: https://www.brentozar.com/ pastetheplan /? id = ryrvodEUX
select c.ID после обновления: https://www.brentozar.com/pastetheplan/?id=B1MRoO487
select * после обновления: https://www.brentozar.com/pastetheplan/?id=Hk7si_V8m
select *по-прежнему превосходит select c.IDпо общей продолжительности и общему количеству чтений ( select *около половины чтений), но использует больше ресурсов ЦП. В целом они гораздо ближе, чем до обновления, однако планы все же различаются.
Такое же поведение наблюдается в 2016 году в режиме совместимости 2014 года и в 2014 году. Чем можно объяснить несоответствие между этими двумя планами? Может ли быть так, что «правильные» индексы не были созданы? Может ли статистика быть немного устаревшей причиной этого?
Я попытался переместить предикаты до ONчасти объединения несколькими способами, но план запроса каждый раз один и тот же.
После перестроения индекса
Я перестроил все индексы трех таблиц, участвующих в запросе. c.IDвсе еще делает большинство чтений (более чем вдвое больше *), но загрузка процессора составляет около половины *версии. c.IDВерсию также попало на данный TempDb сортировкого ATable:
c.ID: https://www.brentozar.com/pastetheplan/?id=HyHIeDO87
* : https://www.brentozar.com/pastetheplan/?id=rJ4deDOIQ
Я также попытался заставить его работать без параллелизма, и это дало мне самый эффективный запрос: https://www.brentozar.com/pastetheplan/?id=SJn9-vuLX
Я заметил количество выполнений операторов ПОСЛЕ большого поиска по индексу, который выполняет упорядочивание, только выполненное 1000 раз в однопоточной версии, но значительно больше в параллельной версии, между 2622 и 4315 выполнениями различных операторов.
select c.IDзапрос намного быстрее, но он все еще выполняет некоторую дополнительную работу, которую выполняетselect *запрос без подсказок.