用嵌套循环提高SQL查询的性能-PostgreSQL[英] Improve performance on SQL query with Nested Loop - PostgreSQL

本文是小编为大家收集整理的关于用嵌套循环提高SQL查询的性能-PostgreSQL的处理方法,想解了用嵌套循环提高SQL查询的性能-PostgreSQL的问题怎么解决?用嵌套循环提高SQL查询的性能-PostgreSQL问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

我正在使用 PostgreSQL,但我的 SQL 查询有一个奇怪的问题.取决于我使用的日期参数.我的请求没有做同样的操作.

这是我的工作查询:

SELECT DISTINCT app.id_application 
FROM stat sj
LEFT OUTER JOIN groupe gp ON gp.id_groupe = sj.id_groupe 
LEFT OUTER JOIN application app ON app.id_application = gp.id_application 
WHERE date_stat >= '2016/3/01' 
AND date_stat <= '2016/3/31' 
AND ( date_stat = date_gen-1 or (date_gen = '2016/04/01' AND date_stat = '2016/3/31')) 
AND app.id_application IS NOT NULL 

这个查询大约需要 2 秒(这对我来说没问题,因为我有很多行).当我为此查询运行 EXPLAIN ANALYZE 时,我有这个:

HashAggregate  (cost=375486.95..375493.62 rows=667 width=4) (actual time=2320.541..2320.656 rows=442 loops=1)
    ->  Hash Join  (cost=254.02..375478.99 rows=3186 width=4) (actual time=6.144..2271.984 rows=263274 loops=1)
    Hash Cond: (gp.id_application = app.id_application)
    ->  Hash Join  (cost=234.01..375415.17 rows=3186 width=4) (actual time=5.926..2200.671 rows=263274 loops=1)
          Hash Cond: (sj.id_groupe = gp.id_groupe)
          ->  Seq Scan on stat sj  (cost=0.00..375109.47 rows=3186 width=8) (actual time=3.196..2068.357 rows=263274 loops=1)
                Filter: ((date_stat >= '2016-03-01'::date) AND (date_stat <= '2016-03-31'::date) AND ((date_stat = (date_gen - 1)) OR ((date_gen = '2016-04-01'::date) AND (date_stat = '2016-03-31'::date))))
                Rows Removed by Filter: 7199514
          ->  Hash  (cost=133.45..133.45 rows=8045 width=12) (actual time=2.677..2.677 rows=8019 loops=1)
                Buckets: 1024  Batches: 1  Memory Usage: 345kB
                ->  Seq Scan on groupe gp  (cost=0.00..133.45 rows=8045 width=12) (actual time=0.007..1.284 rows=8019 loops=1)
    ->  Hash  (cost=11.67..11.67 rows=667 width=4) (actual time=0.206..0.206 rows=692 loops=1)
          Buckets: 1024  Batches: 1  Memory Usage: 25kB
          ->  Seq Scan on application app  (cost=0.00..11.67 rows=667 width=4) (actual time=0.007..0.101 rows=692 loops=1)
                Filter: (id_application IS NOT NULL)
    Total runtime: 2320.855 ms

现在,当我尝试对当月进行相同的查询时(我们是 4 月 6 日,所以我正在尝试获取 4 月的所有 application_id)使用相同的查询

SELECT DISTINCT app.id_application 
FROM stat sj
LEFT OUTER JOIN groupe gp ON gp.id_groupe = sj.id_groupe 
LEFT OUTER JOIN application app ON app.id_application = gp.id_application 
WHERE date_stat >= '2016/04/01' 
AND date_stat <= '2016/04/30' 
AND ( date_stat = date_gen-1 or ( date_gen = '2016/05/01' AND date_job = '2016/04/30')) 
AND app.id_application IS NOT NULL 

此查询现在需要 120 秒.所以我也在这个查询上运行了 EXPLAIN ANALYZE,现在它没有相同的操作:

HashAggregate  (cost=375363.50..375363.51 rows=1 width=4) (actual time=186716.468..186716.532 rows=490 loops=1)
->  Nested Loop  (cost=0.00..375363.49 rows=1 width=4) (actual time=1.945..186619.404 rows=118990 loops=1)
    Join Filter: (gp.id_application = app.id_application)
    Rows Removed by Join Filter: 82222090
    ->  Nested Loop  (cost=0.00..375343.49 rows=1 width=4) (actual time=1.821..171458.237 rows=118990 loops=1)
          Join Filter: (sj.id_groupe = gp.id_groupe)
          Rows Removed by Join Filter: 954061820
          ->  Seq Scan on stat sj  (cost=0.00..375109.47 rows=1 width=8) (actual time=0.235..1964.423 rows=118990 loops=1)
                Filter: ((date_stat >= '2016-04-01'::date) AND (date_stat <= '2016-04-30'::date) AND ((date_stat = (date_gen - 1)) OR ((date_gen = '2016-05-01'::date) AND (date_stat = '2016-04-30'::date))))
                Rows Removed by Filter: 7343798
          ->  Seq Scan on groupe gp  (cost=0.00..133.45 rows=8045 width=12) (actual time=0.002..0.736 rows=8019 loops=118990)
    ->  Seq Scan on application app  (cost=0.00..11.67 rows=667 width=4) (actual time=0.003..0.073 rows=692 loops=118990)
          Filter: (id_application IS NOT NULL)
  Total runtime: 186716.635 ms

因此,我决定通过减少查询中的条件数量,直到性能再次可以接受,来搜索问题出在哪里.

所以只有这个参数

WHERE date_stat >= '2016/04/01'

只需要 1.9 秒(就像第一个工作查询一样)它还使用 2 个参数:

WHERE date_stat >= '2016/04/01' 
AND app.id_application IS NOT NULL 

但是当我尝试添加其中一行时,我在解释中有嵌套循环

AND date_stat <= '2016/04/30' 
AND ( date_stat = date_gen-1 or ( date_gen = '2016/05/01' AND date_stat = '2016/04/30')) 

有人知道它的来源吗?

推荐答案

好的,看起来优化器估计有问题.他认为 4 月只有 1 row,所以他选择 NESTED LOOP,这对于大量行(在这种情况下为 118,990)非常低效.

  1. 对每个表执行 VACUUM ANALYZE.这将清理死元组并刷新统计信息.
  2. 考虑添加基于 dates 的索引,例如 CREATE INDEX date_stat_idx ON <table with date_stat> USING btree (date_stat);

重新运行查询,

本文地址:https://www.itbaoku.cn/post/1763866.html