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 INNULL在值与包含的列表中的任何内容都不匹配时返回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将返回3NOT IN将不返回任何内容(因为它将始终计算为FALSENULL)。

MySQL,如果非空的列,LEFT JOIN / IS NULL并且NOT IN是一点点(百分之几)比更有效NOT EXISTS。如果该列可为空,NOT EXISTS则它是最有效的(同样,效率不高)。

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

SQL ServerNOT IN/ NOT EXISTS更高效,因为LEFT JOIN / IS NULL不能被优化成ANTI JOIN通过其优化器。

在中PostgreSQLLEFT JOIN / IS NULL并且NOT EXISTSNOT INsine 更有效,它们Anti JoinNOT 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.