MySQL "NOT IN"


181

Я хотів запустити простий запит, щоб викинути всі рядки, Table1де головне значення стовпця відсутнє в стовпчику в іншій таблиці ( Table2).

Я спробував використовувати:

SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal

Це замість того, щоб викинути синтаксичну помилку. Пошук у Google привів мене до форумів, де люди говорили, що MySQL не підтримує NOT INі потрібно використовувати щось надзвичайно складне. Це правда? Або я роблю жахливу помилку?


1
А що робити, якщо я хочу подібні дані з трьох таблиць. Я маю на увазі, що одна таблиця1 має 2000 записів, а дві інші таблиці 2 та 3 мають 500 записів, всі вони мають загальне поле 'ім'я'. Як ми можемо отримати всі деталі з таблиці 1, які відсутні в таблицях 2 та 3 на основі "імені". Чи можемо ми використати НЕ Двічі, якщо так, як ..?

Відповіді:


310

Щоб використовувати IN, ви повинні мати набір, використовуйте замість цього синтаксис:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)

85
Обережно, коли table2.principalможна NULL. У цьому випадку NOT INзавжди буде повертатися, FALSEтому що NOT INтрактується як <> ALL, який порівнює всі рядки з підзапиту типу Table1.principal <> table2.principal, який не працює при порівнянні з NULL: Table1.principal <> NULLне призведе до TRUE. Щоб виправити: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Басті

4
Дякуємо за коментар @Basti! Ви витратили багато часу, намагаючись зрозуміти, чому запит не працює, як очікувалося.
gvas

3
Не забувайте уникати використання "SELECT *" у списку "NOT IN". Ви повинні вибрати певний стовпець. Інакше ви отримаєте цю помилку: stackoverflow.com/questions/14046838/…
Lorien Brune

165

На варіант запиту вже відповіли, але зауважте, що у багатьох випадках a LEFT JOINможе бути більш швидким способом зробити це:

SELECT table1.*
FROM table1 LEFT JOIN table2 ON table2.principal=table1.principal
WHERE table2.principal IS NULL

Якщо ви хочете перевірити кілька таблиць, щоб переконатися, що вони відсутні в жодній із таблиць (як, наприклад, у коментарі SRKR), ви можете скористатися цим:

SELECT table1.*
FROM table1
LEFT JOIN table2 ON table2.name=table1.name
LEFT JOIN table3 ON table3.name=table1.name
WHERE table2.name IS NULL AND table3.name IS NULL

2
У моїх власних тестах було однакове виконання обох NOT IN& LEFT JOIN. +1 обидва
BufferStack

як тільки запит запустився один раз, ви повинні отримати однакові результати, незалежно від внутрішнього кешування БД
Toote

Для мене виступ був набагато кращим. Я бігав по різних столах, у них були встановлені сторонні ключі.
Кінора Фуффбол

36

НЕ проти та не існує проти лівого приєднання / є нульовим у MySQL

MySQL, як і всі інші системи, крім SQL Server, здатний оптимізувати LEFT JOIN/IS NULL повернути, FALSEяк тільки знайдеться відповідне значення, і це єдина система, яка піклується про документування такої поведінки. […] Оскільки MySQL не в змозі використовувати алгоритми HASHта MERGEприєднуватись, єдиний ANTI JOINвін здатнийNESTED LOOPS ANTI JOIN

[…]

По суті, [ NOT IN] - це абсолютно той самий план, який LEFT JOIN/ IS NULLвикористовується, незважаючи на те, що ці плани виконуються різними гілками коду, і вони виглядають різними за результатами EXPLAIN. Насправді алгоритми однакові, а запити виконуються за один і той же час.

[…]

Важко сказати точну причину [падіння продуктивності при використанні NOT EXISTS] , оскільки це падіння лінійне і, схоже, не залежить від розподілу даних, кількості значень в обох таблицях тощо, доки індекси обох полів індексуються. Оскільки в MySQL є три фрагменти коду, які в основному виконують одну роботу, можливо, що код, який відповідає за це, EXISTSробить якусь додаткову перевірку, яка потребує додаткового часу.

[…]

MySQL може оптимізувати всі три методи для виконання свого роду NESTED LOOPS ANTI JOIN. […] Однак ці три методи генерують три різні плани, які виконуються трьома різними фрагментами коду. Код, який виконує EXISTSпредикат, приблизно на 30% менш ефективний […]

Ось чому найкращим способом пошуку пропущених значень у MySQL є використання LEFT JOIN/ IS NULLабо, NOT INа не NOT EXISTS.

(акценти додані)


7

На жаль, здається, проблема з використанням MySql пункту "НЕ В", знімок екрана нижче показує параметр підзапиту, що повертає неправильні результати:

mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 1.1.8                        |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.5.21                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
7 rows in set (0.07 sec)

mysql> select count(*) from TABLE_A where TABLE_A.Pkey not in (select distinct TABLE_B.Fkey from TABLE_B );
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from TABLE_A left join TABLE_B on TABLE_A.Pkey = TABLE_B.Fkey where TABLE_B.Pkey is null;
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> select count(*) from TABLE_A where NOT EXISTS (select * FROM TABLE_B WHERE TABLE_B.Fkey = TABLE_A.Pkey );
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> 

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