【问题标题】:Postgres Query not selecting index for a column with OR conditionPostgres查询没有为具有OR条件的列选择索引
【发布时间】:2018-05-02 22:54:49
【问题描述】:

我有一个查询,当我使用 OR 条件时,Postgres 正在执行带有序列扫描的哈希连接,而不是带有嵌套循环的索引连接。这导致查询需要 2 秒而不是在

explain analyze
SELECT Count (_pcn.id) AS total_open_note
FROM   patientchartnote _pcn
   INNER JOIN appointment _appt
           ON _appt.id = _pcn.appointment_id
   INNER JOIN patient _pt
           ON _pt.id = _appt.patient_id
   LEFT OUTER JOIN person _ps
                ON _ps.id = _pt.appuser_id
   WHERE  _pcn.active = true
   AND _pt.active = true
   AND _appt.datecomplete IS NULL
   AND _pcn.title IS NOT NULL
   AND _pcn.title <> ''
   AND ( _pt.assigned_to_user_id = '136964'
         OR  _pcn.createdby_id = '136964'
   );


 Aggregate  (cost=237655.59..237655.60 rows=1 width=8) (actual       time=1602.069..1602.069 rows=1 loops=1)
 ->  Hash Join  (cost=83095.43..237645.30 rows=4117 width=4) (actual time=944.850..1602.014 rows=241 loops=1)
 Hash Cond: (_appt.patient_id = _pt.id)
 Join Filter: ((_pt.assigned_to_user_id = 136964) OR (_pcn.createdby_id = 136964))
 Rows Removed by Join Filter: 94036
 ->  Hash Join  (cost=46650.68..182243.64 rows=556034 width=12) (actual time=415.862..1163.812 rows=94457 loops=1)
 Hash Cond: (_pcn.appointment_id = _appt.id)
 ->  Seq Scan on patientchartnote _pcn  (cost=0.00..112794.20 rows=1073978 width=12) (actual time=0.016..423.262 rows=1
073618 loops=1)
Filter: (active AND (title IS NOT NULL) AND ((title)::text <> ''::text))
Rows Removed by Filter: 22488
->  Hash  (cost=35223.61..35223.61 rows=696486 width=8) (actual time=414.749..414.749 rows=692839 loops=1)
Buckets: 131072  Batches: 16  Memory Usage: 2732kB
->  Seq Scan on appointment _appt  (cost=0.00..35223.61 rows=696486 width=8)        (actual time=0.010..271.208 rows=69
2839 loops=1)
Filter: (datecomplete IS NULL)
Rows Removed by Filter: 652426
->  Hash  (cost=24698.57..24698.57 rows=675694 width=12) (actual time=351.566..351.566 rows=674929 loops=1)
Buckets: 131072  Batches: 16  Memory Usage: 2737kB
->  Seq Scan on patient _pt  (cost=0.00..24698.57 rows=675694 width=12) (actual time=0.013..197.268 rows=674929 loops=
1)
Filter: active
Rows Removed by Filter: 17426
Planning time: 1.533 ms
Execution time: 1602.715 ms

当我将“OR _pcn.createdby_id = '136964'”替换为“AND _pcn.createdby_id = '136964'”时,Postgres 会执行索引扫描

 Aggregate  (cost=29167.56..29167.57 rows=1 width=8) (actual time=937.743..937.743 rows=1 loops=1)
 ->  Nested Loop  (cost=1.28..29167.55 rows=7 width=4) (actual time=19.136..937.669 rows=37 loops=1)
 ->  Nested Loop  (cost=0.85..27393.03 rows=1654 width=4) (actual time=2.154..910.250 rows=1649 loops=1)
 ->  Index Scan using patient_activeassigned_idx on patient _pt  (cost=0.42..3075.00 rows=1644 width=8) (actual time=1.
599..11.820 rows=1627 loops=1)
 Index Cond: ((active = true) AND (assigned_to_user_id = 136964))
 Filter: active
 ->  Index Scan using appointment_datepatient_idx on appointment _appt  (cost=0.43..14.75 rows=4 width=8) (actual time=
 0.543..0.550 rows=1 loops=1627)
 Index Cond: ((patient_id = _pt.id) AND (datecomplete IS NULL))
 ->  Index Scan using patientchartnote_activeappointment_idx on patientchartnote _pcn  (cost=0.43..1.06 rows=1 width=8) (actual time=0.014..0.014 rows=0 loops=1649)
 Index Cond: ((active = true) AND (createdby_id = 136964) AND (appointment_id = _appt.id) AND (title IS NOT NULL))
 Filter: (active AND ((title)::text <> ''::text))
 Planning time: 1.489 ms
 Execution time: 937.910 ms
 (13 rows)

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    在 SQL 查询中使用 OR 通常会导致性能下降。

    这是因为 - 与 AND 不同 - 它不限制,而是扩展查询结果中的行数。使用AND,您可以对条件的一部分使用索引扫描,并在第二个条件上使用过滤器进一步限制结果集。 OR 无法做到这一点。

    所以 PostgreSQL 只做剩下的事情:它计算整个连接,然后过滤掉所有不符合条件的行。当然,当您连接三个表时,效率非常低(我没有计算外连接)。

    假设所有名为id 的列都是主键,您可以重写查询如下:

    SELECT count(*) FROM
        (SELECT _pcn.id
         FROM   patientchartnote _pcn
            INNER JOIN appointment _appt
                    ON _appt.id = _pcn.appointment_id
            INNER JOIN patient _pt
                    ON _pt.id = _appt.patient_id
            LEFT OUTER JOIN person _ps
                         ON _ps.id = _pt.appuser_id
            WHERE  _pcn.active = true
            AND _pt.active = true
            AND _appt.datecomplete IS NULL
            AND _pcn.title IS NOT NULL
            AND _pcn.title <> ''
            AND _pt.assigned_to_user_id = '136964'
         UNION
         SELECT _pcn.id
         FROM   patientchartnote _pcn
            INNER JOIN appointment _appt
                    ON _appt.id = _pcn.appointment_id
            INNER JOIN patient _pt
                    ON _pt.id = _appt.patient_id
            LEFT OUTER JOIN person _ps
                         ON _ps.id = _pt.appuser_id
            WHERE  _pcn.active = true
            AND _pt.active = true
            AND _appt.datecomplete IS NULL
            AND _pcn.title IS NOT NULL
            AND _pcn.title <> ''
            AND _pcn.createdby_id = '136964'
        ) q;
    

    尽管这会运行两次查询,但索引可用于在早期过滤掉除几行之外的所有行,因此该查询应该会执行得更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-27
      • 1970-01-01
      • 2020-03-24
      • 2023-03-05
      • 2013-09-15
      • 2021-12-29
      • 1970-01-01
      相关资源
      最近更新 更多