SQL Server IN и EXISTS Производительность

115

Мне любопытно, что из следующего было бы более эффективным?

Я всегда был немного осторожен в использовании, INпотому что считаю, что SQL Server превращает набор результатов в большой IFоператор. Для большого набора результатов это может привести к снижению производительности. Для небольших наборов результатов я не уверен, что это предпочтительнее. Для больших наборов результатов не было EXISTSбы более эффективным?

WHERE EXISTS (SELECT * FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

против

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])
Рэнди Миндер
источник
8
Лучший способ узнать это - попробовать и сделать некоторые измерения.
Клаус Бысков Педерсен
10
там есть , чтобы быть Gazillion дубликатами для этого ......
marc_s
5
@marc_s - Возможно, но за то время, которое мне потребовалось, чтобы просмотреть все сообщения на эту тему и найти тот, который подходит для моего случая, у меня было четыре ответа на свой вопрос.
Рэнди Миндер,
7
К вашему сведению, если вы хотите наиболее производительный способ, вы можете сделать это select 1 from Base...в своем, where existsпоскольку на самом деле вам не важны результаты, а просто то, что строка действительно существует.
Брэд
2
@marc_s, это действительно печально, потому что я нашел время, чтобы просмотреть сообщения, чтобы больше не добавлять мусор в stackoverflow. Мне не нужен индивидуальный ответ, чтобы выполнять свою работу. Такое мышление добавило Газиллион дубликатов вместо нескольких с хорошими ответами
IvoC

Ответы:

140

EXISTS будет быстрее, потому что, как только двигатель обнаружит попадание, он перестанет искать, поскольку условие подтвердилось.

При наличии INон соберет все результаты подзапроса перед дальнейшей обработкой.

keithwarren7
источник
4
Неплохо подмечено. Оператор IN требует, чтобы SQL Server сгенерировал полный набор результатов, а затем, я думаю, создал большой оператор IF.
Рэнди Миндер,
72
Раньше это было правдой, но в текущих версиях (по крайней мере, 2008) оптимизатор намного умнее ... он фактически обрабатывает IN () так же, как EXISTS ().
Аарон Бертран
11
@Aaron - да, обычно оптимизатор внутренне создает лучший план. Однако использование внутренних ярлыков может оказаться вредным в более сложных сценариях.
Скотт Коутс,
2
Это просто неправильно. Это было в 2010 году и есть до сих пор.
Магнус
2
IN и EXISTS имеют тот же план запроса, что и IO. Нет причин думать, что они разные по производительности. проверь статистику своего времени и убедись в этом
Nelssen
40

Принятый ответ недальновиден, и вопрос в этом немного расплывчатый:

1) Также не указывайте явно, присутствует ли индекс покрытия слева, справа или с обеих сторон.

2) Ни то, ни другое не учитывает размер набора левой стороны ввода и набора правой стороны ввода.
(В вопросе просто упоминается общий большой набор результатов ).

Я считаю, что оптимизатор достаточно умен, чтобы конвертировать между «в» и «существует», когда существует значительная разница в стоимости из-за (1) и (2), в противном случае он может использоваться просто как подсказка (например, существует для поощрения использования индекс с возможностью поиска справа).

Обе формы могут быть преобразованы в формы соединения внутренне, иметь обратный порядок соединения и запускаться как цикл, хэш или слияние - в зависимости от предполагаемого количества строк (слева и справа) и наличия индекса слева, справа или с обеих сторон.

crokusek
источник
3
не знаю, почему этот отличный ответ не привлек больше внимания. Понимание индекса / структуры для обеих сторон может повлиять. Я согласен. Хорошо сказано.
SheldonH
Оптимизатор всегда дает один и тот же план для INи EXISTS. Попробуйте придумать любой случай, когда у них другой план (хотя это не относится к NOT INи NOT EXISTS)
Мартин Смит
@MartinSmith Я полагаю, вы знаете, о чем говорите, но есть ли у вас доказательства того, что планы всегда одни и те же? Если так, то разногласия, которые длились десять лет, здесь прояснились.
MarredCheese
@MarredCheese - бремя ответственности лежит на людях, которые утверждают, что создание единственного примера
Мартин Смит
37

Я провел некоторое тестирование SQL Server 2005 и 2008, и как EXISTS, так и IN вернулись с точно таким же фактическим планом выполнения, как утверждали другие. Оптимизатор оптимален. :)

Однако следует помнить о том, что EXISTS, IN и JOIN могут иногда возвращать разные результаты, если вы неправильно формулируете свой запрос: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx

Адам Нофсингер
источник
5

Здесь есть много вводящих в заблуждение ответов, в том числе один, получивший высокую оценку (хотя я не верю, что их операции навредили). Короткий ответ: это то же самое.

В языке (T-) SQL есть много ключевых слов, но, в конце концов, единственное, что действительно происходит на оборудовании, - это операции, указанные в плане выполнения запроса.

Операция отношения (математическая теория), которую мы выполняем, когда мы вызываем, [NOT] INи [NOT] EXISTSявляется полусоединением (антисоединением при использовании NOT). Неслучайно соответствующие операции sql-сервера имеют одно и то же имя . Нет операции, которая упоминает INили EXISTSгде-либо - только (анти) полусоединения. Таким образом, нет никакого способа, чтобы логически эквивалентный выбор INvs EXISTSмог повлиять на производительность, потому что есть один и единственный способ, операция выполнения (анти) полусоединения, для получения их результатов. .

Пример:

Запрос 1 ( план )

select * from dt where dt.customer in (select c.code from customer c where c.active=0)

Запрос 2 ( план )

select * from dt where exists (select 1 from customer c where c.code=dt.customer and c.active=0)
Джордж Менутис
источник
Вы это проверяли? Если да, можете ли вы поделиться своим SQL и результатами?
UnhandledExcepSean
Проверял несколько раз. Я могу создать другой тестовый пример, и я это сделаю, но тестовый пример не означает, что оптимизатор будет выполнять тот же план для таблиц с разной статистикой. Это может заставить кого-то подумать, что ответ частичный, но отсутствие нескольких операторов полусоединения - факт. Может, найду где-нибудь список и свяжу его.
Джордж Менутис
5

Я бы выбрал EXISTS через IN, см. Ссылку ниже:

SQL Server: JOIN vs IN vs EXISTS - логическая разница

Существует распространенное заблуждение, что IN ведет себя одинаково с EXISTS или JOIN с точки зрения возвращаемых результатов. Это просто неправда.

IN: возвращает true, если указанное значение соответствует любому значению в подзапросе или списке.

Существует: возвращает true, если подзапрос содержит какие-либо строки.

Присоединиться: объединяет 2 набора результатов в столбце соединения.

Автор блога: https://stackoverflow.com/users/31345/mladen-prajdic

дубильщик
источник
Вау, спасибо за ваш блог и объяснения.
Кристиан Мюллер,
3

Планы выполнения в этих случаях обычно будут идентичными, но пока вы не увидите, как оптимизатор учитывает все другие аспекты индексов и т. Д., Вы никогда не узнаете.

Кейд Ру
источник
3

Таким образом, IN - это не то же самое, что EXISTS, и он не будет производить тот же план выполнения.

Обычно EXISTS используется в коррелированном подзапросе, что означает, что вы ПРИСОЕДИНЯЕТЕСЬ к внутреннему запросу EXISTS со своим внешним запросом. Это добавит больше шагов для получения результата, так как вам нужно решить соединения внешнего запроса и соединения внутреннего запроса, а затем сопоставить их предложения where для объединения обоих.

Обычно IN используется без корреляции внутреннего запроса с внешним запросом, и это может быть решено всего за один шаг (в лучшем случае).

Учти это:

  1. Если вы используете IN, а результатом внутреннего запроса являются миллионы строк с разными значениями, он, вероятно, будет выполнять МЕНЬШЕ, чем EXISTS, учитывая, что запрос EXISTS является производительным (имеет правильные индексы для соединения с внешним запросом).

  2. Если вы используете EXISTS и соединение с вашим внешним запросом является сложным (требуется больше времени для выполнения, нет подходящих индексов), он замедлит запрос на количество строк во внешней таблице, иногда расчетное время для завершения может быть в днях. Если количество строк приемлемо для вашего оборудования или количество данных правильное (например, меньше значений DISTINCT в большом наборе данных), IN может работать быстрее, чем EXISTS.

  3. Все вышеперечисленное будет отмечено, когда у вас будет достаточное количество строк в каждой таблице (честно говоря, я имею в виду то, что превышает пороги обработки вашего процессора и / или RAM для кэширования).

Итак, ОТВЕТ ЗАВИСИТ. Вы можете написать сложный запрос внутри IN или EXISTS, но, как правило, вы должны попытаться использовать IN с ограниченным набором различных значений и EXISTS, когда у вас много строк с большим количеством различных значений.

Хитрость заключается в том, чтобы ограничить количество сканируемых строк.

С Уважением,

MarianoC

MarianoC
источник
1

Чтобы оптимизировать EXISTS, будьте очень буквальными; что-то просто должно быть там, но на самом деле вам не нужны какие-либо данные, возвращаемые из коррелированного подзапроса. Вы просто оцениваете логическое условие.

Так:

WHERE EXISTS (SELECT TOP 1 1 FROM Base WHERE bx.BoxID = Base.BoxID AND [Rank] = 2)

Поскольку коррелированный подзапрос равен RBAR, первое попадание результата делает условие истинным, и оно больше не обрабатывается.

Джош Льюис
источник
Я всегда был бы крайне осторожен при использовании кодирования LEFT JOIN + NULL, потому что очень легко получить пропущенные или искаженные результаты, если вы не будете очень осторожны в обработке NULL. Я очень редко встречал ситуацию, когда EXISTS или CTE (для поиска дублирования или синтетической вставки для отсутствующих данных) не удовлетворяют тем же требованиям и не превосходят LEFT JOIN + NULL
Джош Льюис
3
TOP 1 должен быть полностью лишним (или избыточным по событиям) при использовании с EXISTS. EXISTS всегда возвращается, как только находит подходящую строку.
Карл Кенингер
Пока что я не заметил прироста производительности при таком подходе. Покажите, пожалуйста, несколько скриншотов планов выполнения
DaFi4
-1

Совершенно неожиданно и не гарантирую, что он верен: я считаю, что в этом случае второй будет быстрее.

  1. В первом случае коррелированный подзапрос, вероятно, вызовет выполнение подзапроса для каждой строки.
  2. Во втором примере подзапрос должен выполняться только один раз, так как он не коррелирован.
  3. Во втором примере INпроизойдет короткое замыкание, как только будет найдено совпадение.
RedFilter
источник