为什么在SQL查询中IN IN比IN慢得多

时间:2018-04-06 21:21:33

标签: sql postgresql query-optimization notin

我发现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[]))

然而,当我因为没有进入而改变时,这只是一个否定:

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只是一个否定它可以再次使用散列连接而只是反过来:当嵌套select中有processInstanceId时使用IN,将其添加到结果中,当没有时,不添加它,使用NOT IN当嵌套选择中有processInstanceId时,不要将其添加到结果中,如果没有,则将其添加到结果中。

那你可以解释为什么会这样吗?澄清AuditTaskImpl的processInstanceId属性也存在于ProcessInstanceLog表中,尽管它们之间没有外键关系。

感谢。

1 个答案:

答案 0 :(得分:3)

如果子查询中的任何值为NOT IN,则NULL的语义要求返回 nothing 。因此,Postgres需要查看所有值。

我强烈建议永远不要将NOT IN与子查询一起使用。 始终使用NOT EXISTS

DELETE FROM AuditTaskImpl l
    WHERE NOT EXISTS (SELECT 1 
                      FROM ProcessInstanceLog spl
                      WHERE l.processInstanceId = spl.spl.processInstanceId AND
                            spl.status not in (2, 3)
                     );