У меня есть запрос ниже:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
Вышеуказанный запрос завершается за три секунды.
Если приведенный выше запрос возвращает какое-либо значение, мы хотим, чтобы хранимая процедура завершила работу, поэтому я переписал его, как показано ниже:
If Exists(
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source)
)
Begin
Raiserror('Source missing',16,1)
Return
End
Однако это занимает 10 минут.
Я могу переписать вышеуказанный запрос, как показано ниже, который также выполняется менее чем за 3 секунды:
select databasename
from somedb.dbo.bigtable l where databasename ='someval' and source <>'kt'
and not exists(select 1 from dbo.smalltable c where c.source=l.source
if @@rowcount >0
Begin
Raiserror('Source missing',16,1)
Return
End
Проблема с перезаписью выше заключается в том, что указанный выше запрос является частью более крупной хранимой процедуры и возвращает несколько наборов результатов. В C # мы перебираем каждый набор результатов и выполняем некоторую обработку.
Вышеприведенное возвращает пустой набор результатов, поэтому, если я пойду с этим подходом, мне придется изменить свой C # и снова выполнить развертывание.
Итак, мой вопрос,
почему использование просто
IF EXISTS
меняет план так долго?
Ниже приведены подробности, которые могут вам помочь, и дайте мне знать, если вам нужны какие-либо подробности:
- Создайте скрипт таблицы и статистики, чтобы получить тот же план, что и у меня
- План медленного исполнения
План быстрого исполнения
Медленный план с помощью Brentozar Вставить план
Быстрый план с помощью Brentozar Вставить план
Примечание: оба запроса одинаковы (с использованием параметров), единственное отличие состоит в том, что EXISTS
(возможно, я допустил некоторые ошибки при анонимизации).
Скрипты создания таблицы приведены ниже:
http://pastebin.com/CgSHeqXc - статистика малых таблиц
http://pastebin.com/GUu9KfpS - статистика больших таблиц
источник
Ответы:
Как было объяснено Пол Уайт в своем блоге: Внутри Оптимизатор: Роу цели в Глубины в
EXISTS
Вносит ряд цель, которая предпочитаетNESTED LOOPS
илиMERGE JOIN
болееHASH MATCH
В вашем запросе это, по-видимому, приводит к появлению вложенных циклов и удалению параллелизма, что приводит к более медленному плану.
Таким образом, вам, вероятно, потребуется найти способ переписать ваш запрос без использования
NOT EXISTS
запроса.Вы можете сойти с рук, переписав запрос с помощью a
LEFT OUTER JOIN
и проверив, что в smalltable нет строки, протестировавNULL
Вы также можете использовать
EXCEPT
запрос, в зависимости от того, сколько полей вам нужно сравнить, например:Напомним, у Аарона Бертранда есть запись в блоге, в которой приводятся причины, по которым он предпочитает «НЕ СУЩЕСТВУЕТ», которую вы должны прочитать, чтобы увидеть, работают ли другие подходы лучше, и знать о потенциальных проблемах правильности в случае значений NULL.
Связанные вопросы и ответы: если EXISTS занимает больше времени, чем встроенный оператор выбора
источник
Вам нужно переписать ваш запрос, используя явные объединения, и указать, какую операцию соединения вы хотите использовать (цикл, хэш или объединение), как это.
При использовании EXISTS или NOT EXISTS план запроса, сгенерированный SQL Server, с операцией NESTED LOOP, предполагая, что он должен пройти по всем строкам в наборе одна за другой, ища первую строку, чтобы удовлетворить условию. Использование HASH JOIN ускорит его.
источник
Я столкнулся с той же проблемой, мне удалось обойти себя, избегая использования «EXISTS» и используя функцию «COUNT ()» и оператор «IF ... ELSE».
Для вашего примера попробуйте следующее:
Причина, по которой я добавляю «+ 1» к счетчику, заключается в том, что я могу использовать «> 1» в условии IF, использование «> 0» или «<> 0» заставит запрос использовать вложенные циклы вместо HASH Совпадение. Не задумывался, почему это происходит, было бы интересно узнать почему.
Надеюсь, это поможет!
источник