【发布时间】:2018-04-06 21:21:33
【问题描述】:
我发现了一个令人惊讶的(至少对我而言)IN 和 NOT IN 的事情。当我尝试解释 PostgreSQL 数据库的第一个查询时:
EXPLAIN DELETE
FROM AuditTaskImpl l
WHERE l.processInstanceId in (select spl.processInstanceId
FROM ProcessInstanceLog spl
WHERE spl.status not in ( 2, 3))
它告诉我这个:
Delete on audittaskimpl l (cost=2794.48..6373.52 rows=50859 width=12)
-> Hash Semi Join (cost=2794.48..6373.52 rows=50859 width=12)
Hash Cond: (l.processinstanceid = spl.processinstanceid)
-> Seq Scan on audittaskimpl l (cost=0.00..2005.59 rows=50859 width=14)
-> Hash (cost=1909.24..1909.24 rows=50899 width=14)
-> Seq Scan on processinstancelog spl (cost=0.00..1909.24 rows=50899 width=14)
Filter: (status <> ALL ('{2,3}'::integer[]))
但是,当我换 in for not in 时,这只是一个否定:
EXPLAIN DELETE
FROM AuditTaskImpl l
WHERE l.processInstanceId NOT in (select spl.processInstanceId
FROM ProcessInstanceLog spl
WHERE spl.status not in ( 2, 3))
它告诉我这个:
Delete on audittaskimpl l (cost=0.00..63321079.15 rows=25430 width=6)
-> Seq Scan on audittaskimpl l (cost=0.00..63321079.15 rows=25430 width=6)
Filter: (NOT (SubPlan 1))
SubPlan 1
-> Materialize (cost=0.00..2362.73 rows=50899 width=8)
-> Seq Scan on processinstancelog spl (cost=0.00..1909.24 rows=50899 width=8)
Filter: (status <> ALL ('{2,3}'::integer[]))
如您所见,使用 IN 它使用哈希连接,这当然要快得多,但使用 NOT IN 它只使用简单的逐行顺序扫描。但是由于 NOT IN 只是一个否定,它可以再次使用哈希连接并做相反的事情:当嵌套选择中有 processInstanceId 时使用 IN ,将其添加到结果中,当没有时,不要添加它,使用 NOT IN嵌套select中有processInstanceId时,不要添加到结果中,没有时添加到结果中。
那么你能解释一下为什么会发生这种情况吗?澄清 AuditTaskImpl 具有 processInstanceId 属性,该属性也存在于 ProcessInstanceLog 表中,尽管它们之间没有外键关系。
谢谢。
【问题讨论】:
-
NOT IN必须考虑可能出现在任何行中的NULL值。请改用NOT EXISTS。 -
NOT IN 不只是否定,它涉及 三值逻辑 参见en.wikipedia.org/wiki/Three-valued_logic 或例如modern-sql.com/concept/three-valued-logic 简而言之:
NOT IN是邪恶的。
标签: sql postgresql query-optimization notin