SQL“select不在子查询中”不返回任何结果
免责声明:我已经find了问题(我认为),但是我想把这个问题添加到堆栈溢出,因为我无法(很容易)在任何地方find它。 另外,有人可能比我有更好的答案。
我有一个数据库,其中一个表“通用”被其他几个表引用。 我想看看Common表中的哪些logging是孤立的(即没有任何其他表的引用)。
我运行这个查询:
select * from Common where common_id not in (select common_id from Table1) and common_id not in (select common_id from Table2)
我知道有孤立的logging,但没有logging被返回。 为什么不?
(如果有问题,这是SQL Server。)
更新:
我的博客中的这些文章更详细地描述了方法之间的差异:
-
NOT IN
与NOT EXISTS
与LEFT JOIN / IS NULL
:SQL Server
-
NOT IN
与NOT EXISTS
与LEFT JOIN / IS NULL
:PostgreSQL
-
NOT IN
与NOT EXISTS
与LEFT JOIN / IS NULL
:Oracle
-
NOT IN
与NOT EXISTS
与LEFT JOIN / IS NULL
:MySQL
有三种方法可以做这样的查询:
-
LEFT JOIN / IS NULL
:SELECT * FROM common LEFT JOIN table1 t1 ON t1.common_id = common.common_id WHERE t1.common_id IS NULL
-
NOT EXISTS
:SELECT * FROM common WHERE NOT EXISTS ( SELECT NULL FROM table1 t1 WHERE t1.common_id = common.common_id )
-
NOT IN
:SELECT * FROM common WHERE common_id NOT IN ( SELECT common_id FROM table1 t1 )
当table1.common_id
不可为空时,所有这些查询在语义上是相同的。
当它可以为空时, NOT IN
是不同的,因为当一个值与包含NULL
的列表中的任何值不匹配时, IN
(和因此NOT IN
)都返回NULL
。
这可能会令人困惑,但如果我们回想一下这个替代语法,可能会变得更加明显:
common_id = ANY ( SELECT common_id FROM table1 t1 )
这个条件的结果是列表中所有比较的布尔乘积。 当然,单个NULL
值会产生NULL
结果,也就是说,整个结果也是NULL
。
我们从来不能肯定地说common_id
不等于这个列表中的任何东西,因为至less有一个值是NULL
。
假设我们有这些数据:
common -- 1 3 table1 -- NULL 1 2
LEFT JOIN / IS NULL
和NOT EXISTS
将返回3
, NOT IN
将不返回任何内容 (因为它总是会计算为FALSE
或NULL
)。
在MySQL
,如果是不可为空的列,则LEFT JOIN / IS NULL
和NOT IN
比NOT EXISTS
更有效一些(百分之几)。 如果该列可以为空,那么NOT EXISTS
是最有效的(再次,不是很多)。
在Oracle
,所有三个查询产生相同的计划( ANTI JOIN
)。
在SQL Server
, NOT IN
/ NOT EXISTS
效率更高,因为LEFT JOIN / IS NULL
无法通过其优化器优化为ANTI JOIN
。
在PostgreSQL
, LEFT JOIN / IS NULL
和NOT EXISTS
比NOT IN
更有效率,因为它们被优化为Anti Join
,而NOT IN
使用hashed subplan
(如果子查询太大而无法散列)
如果你希望这个世界是一个二值布尔值的地方,你必须自己阻止这个空值(第三个值)。
不要写IN子句列表中允许空值的子句。 过滤出来!
common_id not in ( select common_id from Table1 where common_id is not null )
表1或表2有一些common_id的空值。 改用这个查询:
select * from Common where common_id not in (select common_id from Table1 where common_id is not null) and common_id not in (select common_id from Table2 where common_id is not null)
SELECT T.common_id FROM Common T LEFT JOIN Table1 T1 ON T.common_id = T1.common_id LEFT JOIN Table2 T2 ON T.common_id = T2.common_id WHERE T1.common_id IS NULL AND T2.common_id IS NULL
select * from Common c where not exists (select t1.commonid from table1 t1 where t1.commonid = c.commonid) and not exists (select t2.commonid from table2 t2 where t2.commonid = c.commonid)
就在我头顶…
select c.commonID, t1.commonID, t2.commonID from Common c left outer join Table1 t1 on t1.commonID = c.commonID left outer join Table2 t2 on t2.commonID = c.commonID where t1.commonID is null and t2.commonID is null
我跑了一些testing,这是我的结果wrt @ patmortech的回答和@ rexem的评论。
如果Table1或Table2没有在commonID上build立索引,你会得到一个表扫描,但是@ patmortech的查询仍然是两倍(对于一个100K的行主表)。
如果在commonID上都没有索引,则会得到两个表扫描,差异可以忽略不计。
如果两者都在commonID上build立索引,则“不存在”查询以1/3的时间运行。
让我们假设common_id的这些值:
Common - 1 Table1 - 2 Table2 - 3, null
我们希望Common中的行返回,因为它不存在于任何其他表中。 然而,空投在猴子扳手。
使用这些值,查询等同于:
select * from Common where 1 not in (2) and 1 not in (3, null)
这相当于:
select * from Common where not (1=2) and not (1=3 or 1=null)
这是问题的起点。 当与null比较时, 答案是未知的 。 所以查询简化为
select * from Common where not (false) and not (false or unkown)
错误或未知是未知的:
select * from Common where true and not (unknown)
真正的而不是未知的也是未知的:
select * from Common where unknown
where条件不返回结果未知的logging,所以我们没有得到logging。
解决这个问题的一个方法是使用exists操作符而不是in。Exists永远不会返回unkown,因为它操作的是行而不是列。 (行或者存在,或者不存在;在行级没有这种零歧义!)
select * from Common where not exists (select common_id from Table1 where common_id = Common.common_id) and not exists (select common_id from Table2 where common_id = Common.common_id)
这对我工作:)
从普通select*
哪里
common_id不在(从Table1中selectISNULL(common_id,'dummy-data') )
和common_id不在(从Table2中selectISNULL(common_id,'dummy-data') )
select *, (select COUNT(ID) from ProductMaster where ProductMaster.CatID = CategoryMaster.ID) as coun from CategoryMaster