Продуктивність SQL Server IN проти EXISTS


115

Мені цікаво, що з наведеного нижче було б більш ефективним?

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

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

vs.

WHERE bx.BoxID IN (SELECT BoxID FROM Base WHERE [Rank = 2])

8
Найкращий спосіб це дізнатися - спробувати це і зробити деякі вимірювання.
Клаус Бисков Педерсен

10
для цього має бути газильйон дублікатів ......
marc_s

5
@marc_s - Мабуть, так, але в той час, як мені знадобилося б переглянути всі публікації з цієї теми, і знайти те, що відповідає моєму випадку, у мене було чотири відповіді на моє запитання.
Ренді Міндер

7
FYI, якщо ти хочеш найефективнішого способу, ти можеш це select 1 from Base...зробити, where existsоскільки ти насправді не переймаєшся результатами, просто такий ряд насправді існує.
brad

2
@marc_s це дуже сумно, тому що я знайшов час для перегляду постів, щоб не додавати більше сміття в stackoverflow. Мені не потрібна спеціальна відповідь, щоб виконати свою роботу. Таке мислення, яке додало дублікатів Газліона замість лише кількох із хорошими відповідями
IvoC

Відповіді:


140

EXISTS буде швидше, тому що, як тільки двигун знайде удар, він перестане шукати, як умова виявилася справжньою.

З IN, він збиратиме всі результати з підзапиту перед подальшою обробкою.


4
Це хороший момент. Оператор IN вимагає від SQL Server генерувати повний набір результатів, а потім створити велике твердження IF, я думаю.
Ренді Міндер

72
Це було правдою, але в сучасних версіях (принаймні, 2008 р.) Оптимізатор набагато розумніший ... він насправді трактує IN () так само, як ІСЦІ ().
Аарон Бертран

11
@Aaron - так, зазвичай оптимізатор створить всебічно кращий план. Однак покладатися на внутрішні ярлики може бути згубним у складніших сценаріях.
Скотт Коутс

2
Це просто просто неправильно. Це було в 2010 році і досі є.
Магнус

2
IN і EXISTS мають точно такий же план запитів, і IO. Немає жодних причин думати, що вони різні за своєю ефективністю. перевірити статистику свого часу та поправити себе
Нельсен

40

Прийнята відповідь короткозора, і питання дещо вільне в цьому:

1) Ні явно не згадуйте, чи є індекс покриття в лівій, правій або обох сторонах.

2) Не враховується також розмір набору лівого боку та правого боку введення.
(У питанні просто згадується загальний великий набір результатів ).

Я вважаю, що оптимізатор досить розумний для перетворення між "в" проти "існує", коли є значна різниця у вартості через (1) та (2), інакше він може просто використовуватись як підказки (наприклад, існує для заохочення використання пошуковий індекс з правого боку).

Обидві форми можуть бути перетворені для внутрішньої приєднання форм, змінити порядок з'єднання та виконати цикл, хеш або злиття - на основі оцінених підрахунків рядків (зліва та справа) та наявності індексу в лівій, правій або обох сторонах.


3
не знаю, чому ця чудова відповідь не привернула більше уваги. Розуміння індексу / структури обох сторін може вплинути на згоду. Добре сказано.
SheldonH

Оптимізатор завжди дає однаковий план для INта EXISTS. Спробуйте придумати будь-який випадок, коли вони не мають однакового плану (хоча це не стосується NOT INі NOT EXISTS)
Martin Smith

@MartinSmith Я припускаю, що ви знаєте, про що говорите, але чи є у вас докази того, що плани завжди однакові? Якщо так, то тут було б усунуто десятирічну незгоду.
MarredCheese

@MarredCheese - тяжкий вплив на людей, які стверджують, що інакше можна
Мартін Сміт

37

Я провів тестування на SQL Server 2005 та 2008 рр., І на EXISTS, і на IN повернувся з таким самим реальним планом виконання, як і інші. Оптимізатор є оптимальним. :)

Що слід пам’ятати, ІСНУЮЧИЙ, ІНТЕРНЕТ і ПРИЄДНАЙТЕ іноді можуть повертати різні результати, якщо ви не правильно сформулюєте свій запит: http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210 .aspx


5

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

У мові (T-) SQL існує багато ключових слів, але врешті-решт, єдине, що дійсно відбувається на апаратному забезпеченні, - це операції, як це видно з плану запитів виконання.

Реляційна (теорія математики) операція, яку ми робимо, коли ми викликаємо, [NOT] INі [NOT] EXISTSє напівз'єднанням (антиз'єднання при використанні NOT). Не випадково відповідні операції sql-сервера мають те саме ім'я . Немає жодної операції, про яку згадується INчи EXISTSдеінде - приєднується лише (анти) напівфабрикат. Таким чином, немає жодного способу, щоб логічно еквівалентний вибір INпроти 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

Я б поїхав з ІСНУЮЧИМИ над IN, дивіться нижче посилання:

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

Існує поширена помилкова думка, що IN поводиться однаково з EXISTS або ПРИЄДНАЙТЕСЯ з точки зору повернених результатів. Це просто неправда.

IN: Повертає значення true, якщо вказане значення відповідає будь-якому значенню в підзапиті чи списку.

Існує: Повертає істину, якщо підзапит містить будь-які рядки.

Приєднання: приєднується до 2 наборів результатів у стовпчику приєднання.

Кредит блогу: https://stackoverflow.com/users/31345/mladen-prajdic


Вау, дякую за ваш блог та пояснення.
Крістіан Мюллер

3

Плани виконання, як правило, будуть ідентичними в цих випадках, але поки ви не побачите, як оптимізатор впливає на всі інші аспекти індексів тощо, ви дійсно ніколи не дізнаєтесь.


3

Отже, IN не є тим самим, як EXISTS, і він не буде виробляти той самий план виконання.

Зазвичай EXISTS використовується у співвіднесеному підзапиті, це означає, що ви приєднаєтесь до внутрішнього запиту EXISTS зі своїм зовнішнім запитом. Це додасть більше кроків для отримання результату, оскільки вам потрібно вирішити приєднання зовнішнього запиту, а внутрішній запит приєднується, а потім збігається з їхніми пунктами приєднання обох.

Зазвичай IN використовується без співвідношення внутрішнього запиту із зовнішнім запитом, і це можна вирішити лише за один крок (у кращому випадку).

Врахуйте це:

  1. Якщо ви використовуєте IN, а внутрішній результат запиту становить мільйони рядків відмінних значень, він, ймовірно, буде виконувати РІШНЕ, ніж ІСНУЮЧИЙ, враховуючи, що запит EXISTS є виконавцем (має правильні індекси для з'єднання із зовнішнім запитом).

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

  3. Все вищезазначене буде відзначено, коли у вас буде достатня кількість рядків на кожній таблиці (я справедливо маю на увазі щось, що перевищує ваші процеси та / або пороги оперативної пам’яті для кешування).

Отже, ВІДПОВІДЬ це ЗАВИСИМО. Ви можете написати складний запит всередині IN чи EXISTS, але, як правило, слід намагатися використовувати IN з обмеженим набором чітких значень та EXISTS, коли у вас багато рядків з великою кількістю чітких значень.

Хитрість полягає в обмеженні кількості рядків для сканування.

З повагою,

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 (для пошуку дублювання або синтетичної вставки для відсутніх даних) не відповідають тим самим вимогам і перевершують лівий приєднання + NULL
Джош Льюїс

3
TOP 1 повинен бути повністю стороннім (або надлишковим), коли використовується з EXISTS. EXISTS завжди повертається, як тільки знайде відповідний рядок.
Карл Кієнінгер

Я не бачив жодної користі від виконання цього підходу. Покажіть будь-які скріншоти Планів виконання
DaFi4

-1

Зверху в голові, і я не гарантую, що буде правильно: я вважаю, що другий буде швидшим у цьому випадку.

  1. По-перше, корельований підзапит, ймовірно, призведе до запуску підзапиту для кожного рядка.
  2. У другому прикладі підзапит повинен запускатися лише один раз, оскільки не співвіднесений.
  3. У другому прикладі INзамикання замикається, як тільки воно знайде відповідність.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.