免责声明:我已经找到了问题(我认为),但我想将此问题添加到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,如果重要的话。)
答案 0 :(得分:207)
<强>更新强>
我博客中的这些文章更详细地描述了这些方法之间的差异:
NOT IN
vs. NOT EXISTS
vs. LEFT JOIN / IS NULL
: SQL Server
NOT IN
vs. NOT EXISTS
vs. LEFT JOIN / IS NULL
: PostgreSQL
NOT IN
vs. NOT EXISTS
vs. LEFT JOIN / IS NULL
: Oracle
NOT IN
vs. NOT EXISTS
vs. 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
是不同的,因为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
将返回 nothing (因为它将始终评估为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
使用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函数处理空值时,我们正在获取数据。