sql - 为什么在 SQL 查询中 NOT IN 比 IN 慢很多

标签 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[]))

然而,当我把 in 换成 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,则返回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)
                     );

关于sql - 为什么在 SQL 查询中 NOT IN 比 IN 慢很多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49701128/

相关文章:

php - 在每个查询中写入数据库名称是否真的比在每次页面加载时调用 mysql_select_db() 更快?

java - 如何在基于注释的Hibernate中将主键设置为同一个表中的外键?

sql - 如何从 Rails 中的数据库中选择具有 ID 的不同记录?

sql - 两个字符之间的可变长度子串

SQL:在 Chartio 中将列动态转置为行

sql - Hive Optimizer 在优化 View 查询时是否考虑 View 定义?

sql - WINDOWS转Linux字符集问题

postgresql - 如何检查 PostgreSQL 中的复制延迟?

sql - 将 LIMITed SELECT 和总 COUNT 组合在一起?

Hibernate @OneToOne 即使使用 @Fetch(FetchMode.JOIN) 也会执行多个查询