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

时间:2009-09-10 16:08:28

标签: sql sql-server tsql

免责声明:我已经找到了问题(我认为),但我想将此问题添加到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,如果重要的话。)

11 个答案:

答案 0 :(得分:207)

<强>更新

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


有三种方法可以进行此类查询:

  • 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是不同的,因为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 NULLNOT EXISTS将返回3NOT IN将返回 nothing (因为它将始终评估为FALSENULL)。

MySQL中,如果是非可空列,LEFT JOIN / IS NULLNOT INNOT EXISTS更有效(几个百分点)。如果列可以为空,NOT EXISTS是最有效的(再次,不多)。

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

SQL Server中,NOT IN / NOT EXISTS效率更高,因为LEFT JOIN / IS NULL优化器无法将ANTI JOIN优化为PostgreSQL

LEFT JOIN / IS NULL中,NOT EXISTSNOT IN效率高于Anti Join,正弦值优化为NOT IN,而hashed subplan使用subplan {{1}}(如果子查询太大而无法哈希,则甚至是普通{{1}})

答案 1 :(得分:34)

如果你想让世界成为一个双值布尔位置,你必须自己阻止null(第三个值)的情况。

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

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

答案 2 :(得分:5)

Table1或Table2对于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)

答案 3 :(得分: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 :(得分:3)

就在我头顶......

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

我跑了几个测试,这是我的结果w.r.t. @ patmortech的回答和@rexem的评论。

如果Table1或Table2没有在commonID​​上编入索引,则会得到表扫描,但@ patmortech的查询仍然是两倍(对于100K行主表)。

如果两者都没有在commonID​​上编入索引,则会进行两次表格扫描,差异可以忽略不计。

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

答案 5 :(得分: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

答案 6 :(得分: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进行比较时,the answer is unknown。因此查询缩减为

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

解决这个问题的一种方法是使用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)

答案 7 :(得分:2)

这对我有用:)

  

从Common

中选择*      

其中

     

common_id not in(从表1中选择 ISNULL(common_id,'dummy-data')

     

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

您还可以访问以下链接来了解Anti join

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函数处理空值时,我们正在获取数据。