使用OR的SQL查询比2个单独的查询慢得多

时间:2018-04-07 20:56:05

标签: sql postgresql relational-database query-optimization postgresql-9.3

当我解释以下查询时:

EXPLAIN DELETE
FROM AuditTaskImpl l
WHERE l.processInstanceId IN (SELECT spl.processInstanceId
                              FROM ProcessInstanceLog spl
                              WHERE spl.status IN (2,3))
      OR NOT EXISTS (SELECT spl.processInstanceId
                     FROM ProcessInstanceLog spl
                     WHERE l.processinstanceid = spl.processinstanceid);

它产生:

Delete on audittaskimpl l  (cost=8.61..424652.49 rows=38144 width=6)
  ->  Seq Scan on audittaskimpl l  (cost=8.61..424652.49 rows=38144 width=6)
        Filter: ((hashed SubPlan 1) OR (NOT (SubPlan 2)))
        SubPlan 1
          ->  Index Scan using idx_pinstlog_status on processinstancelog spl  (cost=0.29..8.61 rows=1 width=8)
                Index Cond: (status = ANY ('{2,3}'::integer[]))
        SubPlan 2
          ->  Index Only Scan using idx_pinstlog_pinstid on processinstancelog spl_1  (cost=0.29..8.31 rows=1 width=0)
                Index Cond: (processinstanceid = l.processinstanceid)

所以约有40万次提取。但是因为我使用了OR,理论上我可以分别运行这两个查询,然后对它们进行联合。那么第一个:

EXPLAIN DELETE
FROM AuditTaskImpl l
WHERE l.processInstanceId IN (SELECT spl.processInstanceId
                              FROM ProcessInstanceLog spl
                              WHERE spl.status in (2,3))

产生

Delete on audittaskimpl l  (cost=8.62..2147.72 rows=1 width=12)
  ->  Hash Semi Join  (cost=8.62..2147.72 rows=1 width=12)
        Hash Cond: (l.processinstanceid = spl.processinstanceid)
        ->  Seq Scan on audittaskimpl l  (cost=0.00..2005.59 rows=50859 width=14)
        ->  Hash  (cost=8.61..8.61 rows=1 width=14)
              ->  Index Scan using idx_pinstlog_status on processinstancelog spl  (cost=0.29..8.61 rows=1 width=14)
                    Index Cond: (status = ANY ('{2,3}'::integer[]))

和第二个:

EXPLAIN DELETE
FROM AuditTaskImpl l
WHERE NOT EXISTS (SELECT spl.processInstanceId
                  FROM ProcessInstanceLog spl
                  WHERE l.processinstanceid = spl.processinstanceid);

产生

Delete on audittaskimpl l  (cost=2666.49..5736.94 rows=1 width=12)
  ->  Hash Anti Join  (cost=2666.49..5736.94 rows=1 width=12)
        Hash Cond: (l.processinstanceid = spl.processinstanceid)
        ->  Seq Scan on audittaskimpl l  (cost=0.00..2005.59 rows=50859 width=14)
        ->  Hash  (cost=1781.66..1781.66 rows=50866 width=14)
              ->  Seq Scan on processinstancelog spl  (cost=0.00..1781.66 rows=50866 width=14)

所以总共cca 8k磁盘提取。 两个表都包含cca 50 000行。 DB是PostgreSQL 9.3。例如DML(DELETE FROM ...)但是使用DQL(SELECT ...)它会产生相同的结果。

这里的另一个例子是使用UNION ALL的SELECT:

EXPLAIN SELECT l.id
FROM AuditTaskImpl l
WHERE NOT EXISTS (SELECT spl.processInstanceId
                  FROM ProcessInstanceLog spl
                  WHERE l.processinstanceid = spl.processinstanceid)

UNION ALL

SELECT l.id
FROM AuditTaskImpl l
WHERE l.processInstanceId IN (SELECT spl.processInstanceId
                              FROM ProcessInstanceLog spl
                              WHERE spl.status IN (2,3))

产生

Append  (cost=2616.49..7975.41 rows=2 width=8)
  ->  Hash Anti Join  (cost=2616.49..5827.67 rows=1 width=8)
        Hash Cond: (l.processinstanceid = spl.processinstanceid)
        ->  Seq Scan on audittaskimpl l  (cost=0.00..2005.59 rows=50859 width=16)
        ->  Hash  (cost=1781.66..1781.66 rows=50866 width=8)
              ->  Seq Scan on processinstancelog spl  (cost=0.00..1781.66 rows=50866 width=8)
  ->  Hash Semi Join  (cost=8.62..2147.72 rows=1 width=8)
        Hash Cond: (l_1.processinstanceid = spl_1.processinstanceid)
        ->  Seq Scan on audittaskimpl l_1  (cost=0.00..2005.59 rows=50859 width=16)
        ->  Hash  (cost=8.61..8.61 rows=1 width=8)
              ->  Index Scan using idx_pinstlog_status on processinstancelog spl_1  (cost=0.29..8.61 rows=1 width=8)
                    Index Cond: (status = ANY ('{2,3}'::integer[]))

总共cca 8k提取。为什么SQL查询的OR比2个单独的查询慢得多?也许这是一个优化问题?

感谢您的回复!

1 个答案:

答案 0 :(得分:2)

为什么在一个足够的时候用两个查询浪费时间?

DELETE
  FROM AuditTaskImpl l
  WHERE not exists (
     SELECT null FROM ProcessInstanceLog spl
       WHERE spl.processInstanceId = l.processInstanceId
         and spl.status not IN (2,3))