SQL“选择不在子查询中的位置”不返回结果


130

免责声明:我已经解决了这个问题(我认为),但是我想将此问题添加到Stack Overflow中,因为我无法(轻松地)在任何地方找到它。另外,有人可能比我有更好的答案。

我有一个数据库,其中一个表“ Common”被其他几个表引用。我想查看Common表中的哪些记录是孤立的(即,没有任何其他表的引用)。

我跑了这个查询:

select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)

我知道有孤立的记录,但没有记录返回。为什么不?

(如果需要的话,这是SQL Server。)


这个stackoverflow.com/a/129152/1667619很好地回答了WHY问题。
— Ruchan

Answers:


234

更新:

我的博客中的这些文章更详细地描述了方法之间的区别:


可以通过三种方式进行此类查询:

  • 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不能为空时,所有这些查询在语义上都是相同的。

当该值可为null时,NOT IN它会有所不同,因为IN(并且因此会NOT IN)NULL在值与包含的列表中的任何内容都不匹配时返回NULL。

这可能会令人困惑,但如果我们回想一下这种语法,可能会变得更加明显:

common_id = ANY
(
SELECT  common_id
FROM    table1 t1
)

此条件的结果是列表中所有比较的布尔乘积。当然,单个NULL值会产生NULL结果,也将呈现整个结果NULL。

我们永远不能肯定地common_id说不等于此列表中的任何内容,因为至少一个值是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 INsine 更有效,它们Anti Join在NOT IN使用时被优化为,hashed subplan(或者,subplan如果子查询太大而无法散列,则甚至是简单的)


8
好答案!谢谢!
— StevenMcD

这很棒而且非常有帮助
— kavun 2012年

1
+1是因为四年半以来,这个答案帮助我解决了一个困扰我的问题!
— Carson63000 2014年

@ Carson63000 Snap!在看到这个答案之前,我以为我已经发疯了
— Bobby

1
@IstiaqueAhmed:NOT EXISTS如果其中的查询返回任何行,则计算结果为TRUE。SELECT NULL可能是SELECT *or SELECT 1或其他任何事物,NOT EXISTS谓词不查看行的值,仅对它们进行计数。
— Quassnoi

36

如果您希望世界成为一个二值布尔值的地方,那么您必须自己防止null(第三值)的情况。

不要写IN子句在列表端允许空值。过滤掉它们!

common_id not in
(
  select common_id from Table1
  where common_id is not null
)

6
从句列表中的空值是缺少查询结果的常见原因。
— 艾米B

“与空值比较时,答案是未知的”-来自@Jeremy Stein的答案。从中common_id not in,我们仍然可以拥有的common_id价值NULL。那么,没有结果的问题是否仍然存在?
— Istiaque Ahmed

5

表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)

1
如果一个表中有数据,而另一表中没有,该怎么办?您是否要在其中“和”或“或”?
— 菲利普·凯利2009年

1
我正在寻找任何表中都未引用的记录,所以我想要AND。我会澄清这个问题。
— 杰里米·斯坦因

4
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)

4

就在我的头顶上...

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

我进行了一些测试,这是@patmortech的答案和@rexem的评论的结果。

如果未在commonID​​上对Table1或Table2进行索引,则可以进行表扫描,但是@patmortech的查询仍然快一倍(对于100K行主表)。

如果两个都没有在commonID​​上建立索引,则将获得两次表扫描,并且差异可以忽略不计。

如果两者都在commonID​​上建立索引,则“不存在”查询的运行时间为1/3。


1
在where子句中应为AND。否则,那行得通。
— 杰里米·斯坦

1
根据您的评论更改。“或”选择任一表中的孤儿。
— 奥斯汀·萨洛宁

1
这样更好 顺便说一句,我出于某种原因应该使用外部联接而不是子查询?
— 杰里米·斯坦

3
可读性是主要的。我怀疑会生成更好的执行计划,但没有查询计划,我无法确认。
— 奥斯丁·萨洛宁

2
这种方法更糟糕,因为使用NOT EXISTS-联接导致获取的行超出其所需的行数,然后列的比较结果为null。而且,“不存在”更易于启动。
— OMG小马

3
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

1
这种方法更糟糕,因为使用NOT EXISTS-联接导致获取的行超出其所需的行数,然后列的比较结果为null。它可以工作,但性能不会那么好-可能与将IN与相关子查询一起使用可能会更糟。
— OMG小马

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条件不会返回结果未知的记录,因此我们不会返回任何记录。

解决此问题的一种方法是使用exist运算符,而不是in。Exists永远不会返回未知,因为它对行而不是列进行操作。(行要么存在,要么不存在;在行级别没有任何这种空歧义!)

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)

2

这对我有用:)

从通用中选择*

哪里

common_id不在(选择ISNULL(common_id,'dummy-data')从表1中)

和common_id不在(从Table2中选择ISNULL(common_id,'dummy-data'))


@marlar,子查询始终返回1或0,而不是值列表。那么NOT IN那里的表演如何呢?
— Istiaque Ahmed

0
select *,
(select COUNT(ID)  from ProductMaster where ProductMaster.CatID = CategoryMaster.ID) as coun 
from CategoryMaster

0

我有一个示例,我在其中查找,因为一个表将值保存为双精度值,另一个表则存储为字符串,因此它们将不匹配(或者不进行强制转换则不匹配)。但只有NOT IN。随着SELECT ... IN ...的工作。很奇怪,但是我想我可以分享,以防其他任何人遇到这个简单的解决方法。


0

请按照以下示例了解上述主题:

您也可以访问以下链接来了解反加入

select department_name,department_id from hr.departments dep
where not exists 
    (select 1 from hr.employees emp
    where emp.department_id=dep.department_id
    )
order by dep.department_name;
DEPARTMENT_NAME DEPARTMENT_ID
Benefits    160
Construction    180
Contracting 190
.......

但是,如果NOT IN在这种情况下使用,则不会获得任何数据。

select Department_name,department_id from hr.departments dep 
where department_id not in (select department_id from hr.employees );

没有找到数据

发生这种情况是因为(select department_id from hr.employees)返回空值,并且整个查询被评估为false。如果我们像下面那样稍微更改SQL并使用NVL函数处理空值,就可以看到它。

select Department_name,department_id from hr.departments dep 
where department_id not in (select NVL(department_id,0) from hr.employees )

现在我们正在获取数据:

DEPARTMENT_NAME DEPARTMENT_ID
Treasury    120
Corporate Tax   130
Control And Credit  140
Shareholder Services    150
Benefits    160
....

同样,由于使用NVL函数处理了null值,因此我们正在获取数据。


SQl结果未以表格形式显示,请裸露我。
— Rajesh Sarkar
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.