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 INNOT EXISTSLEFT JOIN / IS NULLSQL Server
  • NOT INNOT EXISTSLEFT JOIN / IS NULLPostgreSQL
  • NOT INNOT EXISTSLEFT JOIN / IS NULLOracle
  • NOT INNOT EXISTSLEFT JOIN / IS NULLMySQL

有三种方法可以做这样的查询:

  • 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 NULLNOT EXISTS将返回3NOT IN将不返回任何内容 (因为它总是会计算为FALSENULL )。

MySQL ,如果是不可为空的列,则LEFT JOIN / IS NULLNOT INNOT EXISTS更有效一些(百分之几)。 如果该列可以为空,那么NOT EXISTS是最有效的(再次,不是很多)。

Oracle ,所有三个查询产生相同的计划( ANTI JOIN )。

SQL ServerNOT IN / NOT EXISTS效率更高,因为LEFT JOIN / IS NULL无法通过其优化器优化为ANTI JOIN

PostgreSQLLEFT JOIN / IS NULLNOT EXISTSNOT 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