MySQL «НЕ В» запрос


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, основываясь на «имени». Можем ли мы использовать NOT IN дважды, если да, то как?

Ответы:


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/…
Брюн

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

после
запуска

Для меня производительность была намного лучше. Я пробежал по разным таблицам, в то время как у них были установлены внешние ключи.
Keenora Fluffball

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 предложения «NOT IN», на снимке экрана ниже показан вариант подзапроса, возвращающий неверные результаты:

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.