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

169

Я хотел запустить простой запрос, чтобы выкинуть все строки 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 дважды, если да, то как?
Теги:

5 ответов

266
Лучший ответ

Чтобы использовать IN, вы должны иметь набор, вместо этого используйте этот синтаксис:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
  • 65
    Осторожно, когда 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) .
  • 3
    Спасибо за комментарий @Basti! Потратил много времени, пытаясь понять, почему запрос не работал так, как ожидалось.
Показать ещё 1 комментарий
148

Опция подзапроса уже ответила, но обратите внимание, что во многих случаях 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
  • 0
    действительно отличный запрос, сэкономил мне много процессорных часов :)
  • 2
    В моих собственных тестах имел одинаковую производительность для NOT IN и LEFT JOIN . +1 оба
Показать ещё 2 комментария
35

NOT IN vs. NOT EXISTS против LEFT JOIN/NULL в MySQL

MySQL, а также все другие системы, кроме SQL Server, могут оптимизировать LEFT JOIN/IS NULL, чтобы вернуть FALSE, как только найденное совпадение будет найдено, и это только для системы, которая хотела бы документировать это поведение. [& hellip;] Поскольку MySQL не способен использовать алгоритмы объединения HASH и MERGE, единственным ANTI JOIN, который он способен, является NESTED LOOPS ANTI JOIN

[& hellip;]

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

[& hellip;]

Трудно сказать точную причину [падения производительности при использовании NOT EXISTS], так как это падение является линейным и, похоже, не зависит от распределения данных, количества значений в обеих таблицах и т.д., пока оба поля индексируются. Поскольку в MySQL есть три части кода, которые очень важны для выполнения одной задачи, возможно, что код, ответственный за EXISTS, делает какую-то дополнительную проверку, которая занимает дополнительное время.

[& hellip;]

MySQL может оптимизировать все три метода, чтобы сделать своего рода NESTED LOOPS ANTI JOIN. [& Hellip;] Однако эти три метода генерируют три разных плана, которые выполняются тремя разными фрагментами кода. Код, выполняющий предикат EXISTS, на 30% менее эффективен [& hellip;]

Вот почему лучший способ поиска отсутствующих значений в 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> 
5

Будьте осторожны NOT IN не является псевдонимом для <> ANY, но для <> ALL!

http://dev.mysql.com/doc/refman/5.0/en/any-in-some-subqueries.html

SELECT c FROM t1 LEFT JOIN t2 USING (c) WHERE t2.c IS NULL

cant 'заменить на

SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)

Вы должны использовать

SELECT c FROM t1 WHERE c <> ANY (SELECT c FROM t2)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню