MySQL“不在”查询
Posted
技术标签:
【中文标题】MySQL“不在”查询【英文标题】:MySQL "NOT IN" query 【发布时间】:2010-12-03 21:44:55 【问题描述】:我想运行一个简单的查询来抛出 Table1
的所有行,其中主列值不存在于另一个表 (Table2
) 的列中。
我尝试使用:
SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal
这反而会引发语法错误。谷歌搜索把我带到了人们说 mysql 不支持NOT IN
并且需要使用非常复杂的东西的论坛。这是真的?还是我犯了一个可怕的错误?
【问题讨论】:
如果我想要三个表中的相似数据怎么办。我的意思是一个表 1 有 2000 个条目,另外两个表 2 和 3 各有 500 个条目,它们都有共同的字段“名称”。我们如何根据“名称”从表 1 中获取表 2 和 3 中不存在的所有详细信息。我们可以使用 NOT IN 两次吗,如果可以的话如何..? 【参考方案1】:要使用 IN,您必须有一个集合,请改用以下语法:
SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
【讨论】:
小心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)
.
感谢@Basti 的评论!花了很多时间试图了解为什么查询没有按预期工作。
不要忘记避免在“NOT IN”列表中使用“SELECT *”。您必须选择特定的列。否则你会得到这个错误:***.com/questions/14046838/…
@Basti,谢谢,我的 SQL 查询符合您的建议。【参考方案2】:
已经回答了子查询选项,但请注意,在许多情况下,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
【讨论】:
在我自己的测试中,NOT IN
和 LEFT JOIN
的性能相同。 +1 两个
一旦查询运行一次,由于内部数据库缓存,您应该得到相同的结果
对我来说性能要好得多。我浏览了不同的表,虽然它们设置了外键。【参考方案3】:
NOT IN vs. NOT EXISTS vs. LEFT JOIN / IS NULL in 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
。
(添加了重点)
【讨论】:
【参考方案4】:不幸的是,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
不能被替换为
SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)
你必须使用
SELECT c FROM t1 WHERE c <> ANY (SELECT c FROM t2)
【讨论】:
以上是关于MySQL“不在”查询的主要内容,如果未能解决你的问题,请参考以下文章