Відмінність EXISTS від IN у SQL?


443

Яка різниця між EXISTSі INSQL в SQL?

Коли ми повинні використовувати EXISTS, і коли ми повинні використовувати IN?

Відповіді:


224

existsКлючове слово може бути використано таким чином, але на самому справі він призначений як спосіб підрахунку слід уникати:

--this statement needs to check the entire table
select count(*) from [table] where ...

--this statement is true as soon as one match is found
exists ( select * from [table] where ... )

Це найбільш корисно, коли у вас є ifумовні висловлювання, оскільки це existsможе бути набагато швидше, ніж count.

inНайкраще використовувати , коли у вас є список статичного пройти:

 select * from [table]
 where [field] in (1, 2, 3)

Коли у вас є таблиця у inвиписці, то має сенс використовувати A join, але в основному це не має значення. Оптимізатор запитів повинен повертати той самий план у будь-якому випадку. У деяких реалізаціях (здебільшого старих, таких як Microsoft SQL Server 2000) inзапити завжди отримуватимуть вкладений план приєднання , тоді як joinзапити використовуватимуть вкладені, об'єднані чи хешовані, якщо це доречно. Більш сучасні реалізації є розумнішими і можуть коригувати план навіть при inвикористанні.


2
Не могли б ви детальніше розповісти про "Коли у вас є таблиця у виписці, має сенс використовувати з'єднання, але це насправді не має значення. Оптимізатор запитів поверне той самий план у будь-якому випадку". Не частина оптимізатора запитів, частина, в якій ви можете використовувати JOINзаміну IN.
farthVader

select * from [table] where [field] in (select [field] from [table2])повертає ті самі результати (і план запиту), що і select * from [table] join [table2] on [table2].[field] = [table].[field].

@Sander це не робить: перший запит повертає всі стовпці table, а другий повертає все з tableі table2. У деяких (здебільшого старих) базах даних SQL inзапит буде реалізовуватися у вигляді вкладеного об'єднання, тоді як joinзапит можна вкладати, об'єднати, хешировать тощо - що завгодно швидше.
Кіт

2
Гаразд, я мав би вказати стовпці в пункті вибору, але слід оновити свою відповідь, оскільки чітко зазначено, що запити "повернуть той самий план у будь-якому випадку".

existsїх можна використовувати в заяві справи, тому вони також можуть бути зручними, тобтоselect case when exists (select 1 from emp where salary > 1000) then 1 else 0 end as sal_over_1000
smooth_smoothie

125

EXISTSпідкаже, чи отримав запит якісь результати. наприклад:

SELECT * 
FROM Orders o 
WHERE EXISTS (
    SELECT * 
    FROM Products p 
    WHERE p.ProductNumber = o.ProductNumber)

IN використовується для порівняння одного значення з кількома і може використовувати буквальні значення, як це:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (1, 10, 100)

Ви також можете використати результати запиту за допомогою цього INпункту:

SELECT * 
FROM Orders 
WHERE ProductNumber IN (
    SELECT ProductNumber 
    FROM Products 
    WHERE ProductInventoryQuantity > 0)

3
Останній запит небезпечний тим, що може вийти з ладу, якщо підзапит не дає результатів. У пункті "in" потрібен принаймні 1 аргумент ...
user2054927

40
@ user2054927 Останній запит правильно не поверне жодних рядків, якщо підзапит не поверне рядків - нічого небезпечного в цьому немає!
Тоні Ендрюс

Найкраща відповідь.
Амінадав Глікштейн

81

На основі оптимізатора правил :

  • EXISTSнабагато швидше, ніж INтоді, коли результати підзапиту дуже великі.
  • INшвидше, ніж EXISTS, коли результати підзапиту дуже малі.

На основі оптимізатора витрат :

  • Різниці немає.

21
Доказ вашого аргументу? Я не думаю, що IN був би швидшим, ніж колись існували!
Наваз

22
@Nawaz Як щодо доказу, чому IN завжди повільніше, ніж ІСНУЄ?
припинення

2
Погано реалізований оптимізатор запитів? Мені здається, щось подібне (правда, не зовсім така ситуація) трапляється в певних RDBM ...
Haroldo_OK

1
EXISTS повертає чисто булеві значення, що завжди швидше, ніж порівнювати рядки або значення, більші за тип BIT / Boolean. IN може бути або не бути булевим порівнянням. Оскільки програмування віддає перевагу використанню EXPLICIT для стабільності (частина кислоти), EXISTS переважно є кращим.
clifton_h

2
Чому це було схвалено стільки разів? Немає абсолютно ніяких причин, чому це твердження, засноване на припущенні, повинно бути правдивим.
Лукас Едер

40

Я припускаю, що ви знаєте, що вони роблять, і, отже, використовуються по-різному, тому я буду розуміти ваше питання як: Коли було б гарною ідеєю переписати SQL для використання IN замість EXISTS або навпаки.

Це справедливе припущення?


Редагувати : Причина, яку я прошу, полягає в тому, що у багатьох випадках ви можете переписати SQL на базі IN, щоб використовувати замість нього EXISTS, і навпаки, а для деяких двигунів бази даних оптимізатор запитів буде ставитись до двох.

Наприклад:

SELECT *
FROM Customers
WHERE EXISTS (
    SELECT *
    FROM Orders
    WHERE Orders.CustomerID = Customers.ID
)

можна переписати на:

SELECT *
FROM Customers
WHERE ID IN (
    SELECT CustomerID
    FROM Orders
)

або з приєднанням:

SELECT Customers.*
FROM Customers
    INNER JOIN Orders ON Customers.ID = Orders.CustomerID

Отже, моє питання все ще стоїть, чи оригінальний плакат цікавить, що робить IN і EXISTS, і, як, таким чином, ним користуватися, чи він запитує, як переписати SQL за допомогою IN, щоб натомість використовувати EXISTS, чи навпаки, буде хорошою ідеєю?


12
Я не знаю про ОП, але хотів би відповісти на це питання! Коли я повинен використовувати EXISTS замість IN із підзапитом, який повертає ідентифікатори?
Рой Тінкер

8
в JOIN, вам знадобитьсяDISTINCT
Jaider

4
чудова демонстрація, але в
основному

28
  1. EXISTSнабагато швидше, ніж INколи результати підзапиту дуже великі.
    INшвидше, ніж EXISTSколи результати підзапиту дуже малі.

    CREATE TABLE t1 (id INT, title VARCHAR(20), someIntCol INT)
    GO
    CREATE TABLE t2 (id INT, t1Id INT, someData VARCHAR(20))
    GO
    
    INSERT INTO t1
    SELECT 1, 'title 1', 5 UNION ALL
    SELECT 2, 'title 2', 5 UNION ALL
    SELECT 3, 'title 3', 5 UNION ALL
    SELECT 4, 'title 4', 5 UNION ALL
    SELECT null, 'title 5', 5 UNION ALL
    SELECT null, 'title 6', 5
    
    INSERT INTO t2
    SELECT 1, 1, 'data 1' UNION ALL
    SELECT 2, 1, 'data 2' UNION ALL
    SELECT 3, 2, 'data 3' UNION ALL
    SELECT 4, 3, 'data 4' UNION ALL
    SELECT 5, 3, 'data 5' UNION ALL
    SELECT 6, 3, 'data 6' UNION ALL
    SELECT 7, 4, 'data 7' UNION ALL
    SELECT 8, null, 'data 8' UNION ALL
    SELECT 9, 6, 'data 9' UNION ALL
    SELECT 10, 6, 'data 10' UNION ALL
    SELECT 11, 8, 'data 11'
  2. Запит 1

    SELECT
    FROM    t1 
    WHERE   not  EXISTS (SELECT * FROM t2 WHERE t1.id = t2.t1id)

    Запит 2

    SELECT t1.* 
    FROM   t1 
    WHERE  t1.id not in (SELECT  t2.t1id FROM t2 )

    Якщо у t1вашому ідентифікаторі є нульове значення, запит 1 знайде їх, але запит 2 не може знайти нульові параметри.

    Я маю на увазі, що INне можна нічого порівнювати з null, тому це не має результату для null, але я EXISTSможу порівнювати все з null.


Ця відповідь є обґрунтованим конспектом настроїв Тома Кіта ( asktom.oracle.com/pls/asktom/… )
Єромій Французький

Я думаю, що ця відповідь заснована на інтуїції, що досить справедливо. Але це не може бути правдивим. Наприклад, майже напевно це не стосується Інгреса , який би розбирав обидва еквівалентні запити SQL на той самий запит QUEL, якому не вистачає «багатства» SQL, коли мова йде про те, що писати одну і ту ж річ кількома способами.
onedaywhen

Ці 2 запити є логічно еквівалентними тоді і лише тоді, коли t2.id визначений як "NOT NULL". Щоб отримати еквівалентність без залежності у визначенні таблиці, другий запит повинен бути "ВИБРАТИ t1. * ВІД t1, де t1.id не в (SELECT t2.id ВІД t2, де t2.id не є нульовим )"
David דודו Markovitz

16

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


10

IN підтримує лише відносини рівності (або нерівності, коли передує НЕ ).
Це синонім = будь-який / = якийсь , наприклад

select    * 
from      t1 
where     x in (select x from t2)
;

EXISTS підтримує варіанти типів відносин, які неможливо виразити за допомогою IN , наприклад -

select    * 
from      t1 
where     exists (select    null 
                  from      t2 
                  where     t2.x=t1.x 
                        and t2.y>t1.y 
                        and t2.z like '℅' || t1.z || '℅'
                  )
;

І на іншій ноті -

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

«Визначення таблиць, точність статистики», конфігурація бази даних та версія оптимізатора впливають на план виконання, а отже, і на показники продуктивності.


Обґрунтування вашого коментаря щодо продуктивності: не орієнтуючись на конкретні СУБД, слід вважати, що оптимізатор повинен розробити те, що найкраще працює.
Манго

9

ExistsКлючове слово оцінює істинним або хибним, але INключове слово порівняти всі значення у відповідному стовпці півдня запиту. Ще один Select 1може бути використаний з Existsкомандою. Приклад:

SELECT * FROM Temp1 where exists(select 1 from Temp2 where conditions...)

Але INменш ефективний, так Existsшвидше.


5

Я думаю,

  • EXISTSце коли потрібно співставити результати запиту з іншим підзапитом. Результати запиту №1 потрібно отримати там, де результати SubQuery відповідають. Вид приєднання. Наприклад, виберіть таблицю №1 клієнтів, які також розмістили таблицю замовлень №2

  • IN - це отримання, якщо значення певного стовпця лежить INу списку (1,2,3,4,5) Напр. Вибір клієнтів, які лежать у наступних поштових кодах, тобто значення zip_code, лежить у списку (....).

Коли користуватися одним над іншим ... коли відчуєте, що він читає належним чином (краще повідомляє про наміри).


4

Різниця полягає тут:

select * 
from abcTable
where exists (select null)

Вище запит поверне всі записи, тоді як нижче одного повернеться порожніми.

select *
from abcTable
where abcTable_ID in (select null)

Спробуйте спробувати і спостерігати за результатами.


1
Хм ... помилка: [SQL0104] Токен) недійсна. В обох випадках. Ви припускаєте конкретний RDBMS?
jmarkmurphy

3

Згідно з моїми знаннями, коли підзапит повертає NULLзначення, тоді стає все твердження NULL. У таких випадках ми використовуємо EXITSключове слово. Якщо ми хочемо порівняти окремі значення в підзапитах, тоді ми використовуємо INключове слово.


3

Який з них швидший, залежить від кількості запитів, отриманих внутрішнім запитом:

  • Коли ваш внутрішній запит отримує тисячу рядків, тоді EXIST буде кращим вибором
  • Коли ваш внутрішній запит набере кілька рядків, то ІН буде швидше

ІСНУЮТЬ оцінювати за істинним чи хибним, але IN порівнювати кратне значення. Якщо ви не знаєте, чи існує запис чи немає, слід вибрати EXIST


3

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

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

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

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


1

Я розумію, що обидва повинні бути однаковими, доки ми не маємо справу з значеннями NULL.

Ця ж причина, чому запит не повертає значення для = NULL vs NULL. http://sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in/

Що стосується аргументу булева проти компаратора, то для створення булевих обох значень потрібно порівнювати, і це так, як будь-яке, якщо умова працює.



0

Якщо підзапит повертає більше ніж одне значення, можливо, вам доведеться виконати зовнішній запит - якщо значення в стовпці, зазначеному в умові, відповідають будь-якому значенню в наборі результатів підзапиту. Для виконання цього завдання потрібно використовувати inключове слово.

Ви можете використовувати підзапит, щоб перевірити, чи існує набір записів. Для цього вам потрібно скористатись existsпунктом із підзапитом. existsКлючове слово завжди повертає істинне або помилкове значення.


0

Я вважаю, що це відповідь прямо. Чому б ти не перевірив це від людей, які розробили цю функцію у своїх системах?

Якщо ви розробник MS SQL, ось відповідь безпосередньо від Microsoft.

IN:

Визначає, чи відповідає вказане значення будь-якому значенню в підзапиті чи списку.

EXISTS:

Визначає підзапит для перевірки на наявність рядків.



-1

ІСНУЄТЕ Швидше за продуктивністю, ніж IN. Якщо більшість критеріїв фільтра знаходиться в підзапиті, тоді краще використовувати IN і якщо більшість критеріїв фільтра знаходиться в головному запиті, тоді краще використовувати EXISTS.


Це твердження насправді не підкріплене жодними доказами, чи не так?
Лукас Едер

-2

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


@ziggy поясніть? Це майже те, що говорить також прийнята відповідь. У ПОВИНЕНІ перевірити кожен запис, чи існує, що може зупинитися, як тільки він знайде лише один.
Бен Терлі

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