Я пытаюсь понять, почему два запроса, использующие один и тот же план запроса, запрашивающие одни и те же данные, ведут себя по-разному.
Я исследовал следующий запрос.
select T1.id, T2.Num
from DB1.dbo.Table1 T1, DB1.dbo.Table2 T2
where T1.DataDate = (select MAX(datadate) from DB1.dbo.Table1)
and T1.col2 = T2.col2
and T1.col3 = 1
and T2.col4 = 'A string'
order by T1.id
Table1 - это куча с одним уникальным некластеризованным индексом, основанным на данных, col2 и col3. В таблице 150 миллионов строк. Столбец datadate представляет собой столбец char (8), допускающий значение NULL и содержащий значения даты (ггггммдд).
Table2 имеет около 300 строк.
Данные в обеих таблицах были c за последние несколько недель. Статистика не обновлялась ни в одной таблице за последние несколько дней.
Проблема связана с подзапросом
(select MAX(datadate) from DB1.dbo.Table1)
, который генерирует следующую ветвь в предполагаемом плане запроса.
План запроса для сканирования большого индекса
Сюда входит сканирование индекса по уникальному индексу Table1. Это передает все 150 миллионов строк на следующий шаг (Распространение и сбор потоков). Эта версия запроса занимала в среднем 45 секунд.
Я обнаружил, что, добавив предложение «IS NOT NULL», как показано ниже -
(select MAX(datadate) from DB1.dbo.Table1 where datadate is null)
, ветвь плана запроса изменилась на ту, которая показана в приведенной ниже ссылке. При поиске по индексу по уникальному индексу Table1 с передачей одной строки на шаг "Top" без промежуточных шагов потока Distribute или Gather. Эта версия запроса завершилась примерно за 1 секунду.
План запроса для поиска по индексу
Пока все хорошо. Я не могу сказать, что точно понимаю, почему добавление предложения IS NOT NULL должно иметь такой драматический c эффект, но планы запросов для двух разных запросов имели для меня смысл.
Я начал путаться, когда попытался повторно запустить исходный запрос, просто чтобы убедиться, что разница в продолжительности между двумя запросами не была случайностью. Я обнаружил, что выполнение первой версии запроса (без предложения where "IS NOT NULL") в большинстве случаев занимало более 40 секунд, но время от времени оно выполнялось менее чем за 1 секунду.
Когда я посмотрел на фактический план выполнения XML двух прогонов одного и того же запроса («1-секундная версия» и «45-секундная версия»), я обнаружил следующее.
Два запроса использовали один и тот же план запроса. Я основал это на QueryPlanHa sh
QueryHash = "0xCF7F3761DC77476E" QueryPlanHash = "0x55A6B0D6E3D73607" QueryHash = "0xCF7F3761DC77476E" QueryPlanHash6E "номер 10 * 0 * 0 * 0 *, когда он пришел * 0xPlanHash =" *, когда номер * 0x5541A6 = "* 0xPlanHash =" * 0x, когда это * 0x строк, прочитанных при сканировании индекса по уникальному индексу Table1, запрос, который занял более 40 секунд, прочитал все 150 миллионов строк.
Фактический план выполнения XML - длительный запрос
Однако запрос, который занимал 1 секунду, сканировал только часть этих строк.
Фактический план выполнения XML - краткосрочный запрос
Это также может быть видно в графическом фактическом плане выполнения, при этом длительный запрос передает 150 миллионов строк из сканирования индекса
Графический фактический план выполнения - долгосрочный запрос
И " 1-секундная версия "проходит всего 14 000 строк при сканировании индекса.
Графический план фактического выполнения - краткосрочный запрос
Это различное поведение объясняет разницу в продолжительности двух запросов.
Итак, после этого длинное объяснение, мой вопрос:
Как могут два идентичных запроса, используя один и тот же план запроса, запрашивать одни и те же данные (без обновлений таблиц, задействованных между двумя запросами, без статистики обновлений между двумя запросами) вернуть другое количество строк из одного сканирования индекса?