У меня есть запрос, где использование 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 *
запрос без подсказок.Несовершенная статистика, безусловно, может привести к тому, что оптимизатор выберет плохой метод поиска данных. Вы пытались делать
UPDATE STATISTICS ... WITH FULLSCAN
или делать полныйREBUILD
индекс? Попробуйте и посмотрите, поможет ли это.ОБНОВИТЬ
Согласно обновлению от ОП:
Итак, теперь, если единственное предпринятое действие было
UPDATE STATISTICS
, то попробуйте выполнить индексREBUILD
(неREORGANIZE
), как я видел, это помогло с подсчетом количества строк, гдеUPDATE STATISTICS
и индекс, и индексREORGANIZE
не сделали.источник
a) переписать каждую таблицу как подзапрос, следуя этим правилам:
b) SELECT - сначала поставить столбцы соединения
c) PREDICATES - перейти в соответствующие подзапросы
d) ORDER BY - перейти в соответствующие подзапросы, сортируйте на JOIN COLUMNS FIRST
e) Добавьте запрос-обертку для окончательной сортировки и SELECT.
Идея состоит в том, чтобы предварительно отсортировать столбцы соединения внутри каждого подвыбора, поместив сначала столбцы соединения в каждом списке выбора.
Вот что я имею в виду ....
источник
ORDER BY
это недопустимо в подзапросе без TOP, FORXML и т. Д. Я пробовал это безORDER BY
предложений, но это был тот же план.