【问题标题】:Encapsulating Postgres query in view makes it extremely slow在视图中封装 Postgres 查询使其非常慢
【发布时间】:2011-04-01 10:19:54
【问题描述】:

我有一个在 Postgres 8.4 上运行大约 5 秒的查询。它从连接到其他一些表的视图中选择数据,但也使用 lag() 窗口函数,即。

SELECT *, lag(column1) OVER (PARTITION BY key1 ORDER BY ...), lag(...)
FROM view1 v
JOIN othertables USING (...)
WHERE ...

为方便起见,我创建了一个简单的新视图

SELECT *, lag(column1) OVER (PARTITION BY key1 ORDER BY ...), lag(...)
FROM view1 v

然后从中选择,像以前一样使用所有其他 JOIN 和过滤器。令我惊讶的是,这个查询没有在 12 分钟内完成(我当时停止了它)。显然 Postgres 选择了不同的执行计划。我如何让它不这样做,即。使用与原始查询相同的计划?我原以为视图不应该改变执行计划,但显然它确实改变了。

编辑:更重要的是,我发现即使我将第一个视图的内容复制到第二个视图中,仍然不会返回。

编辑 2:好的,我已经充分简化了查询以发布计划。

使用视图(这不会在任何合理的时间内返回):

Subquery Scan sp  (cost=5415201.23..5892463.97 rows=88382 width=370)
  Filter: (((sp.ticker)::text ~~ 'Some Ticker'::text) AND (sp.price_date >= '2010-06-01'::date))
  ->  WindowAgg  (cost=5415201.23..5680347.20 rows=53029193 width=129)
        ->  Sort  (cost=5415201.23..5441715.83 rows=53029193 width=129)
              Sort Key: sp.stock_id, sp.price_date
              ->  Hash Join  (cost=847.87..1465139.61 rows=53029193 width=129)
                    Hash Cond: (sp.stock_id = s.stock_id)
                    ->  Seq Scan on stock_prices sp  (cost=0.00..1079829.20 rows=53029401 width=115)
                    ->  Hash  (cost=744.56..744.56 rows=29519 width=18)
                          ->  Seq Scan on stocks s  (cost=0.00..744.56 rows=29519 width=18)

将窗口函数从视图中取出并放入查询本身(这会立即返回):

WindowAgg  (cost=34.91..34.95 rows=7 width=129)
  ->  Sort  (cost=34.91..34.92 rows=7 width=129)
        Sort Key: sp.stock_id, sp.price_date
        ->  Nested Loop  (cost=0.00..34.89 rows=7 width=129)
              ->  Index Scan using stocks_ticker_unique on stocks s  (cost=0.00..4.06 rows=1 width=18)
                    Index Cond: ((ticker)::text = 'Some Ticker'::text)
                    Filter: ((ticker)::text ~~ 'Some Ticker'::text)
              ->  Index Scan using stock_prices_id_date_idx on stock_prices sp  (cost=0.00..30.79 rows=14 width=115)
                    Index Cond: ((sp.stock_id = s.stock_id) AND (sp.price_date >= '2010-06-01'::date))

因此,在缓慢的情况下,它似乎首先尝试将窗口函数应用于所有数据,然后对其进行过滤,这可能是问题所在。不过,我不知道它为什么会这样。

【问题讨论】:

  • +1,但我强烈怀疑您需要填写... 部分,然后才能诊断出发生了什么。
  • 我可以,但是如果查询相当复杂,我还必须解释所有相关表的外观。我的问题更多是关于这个而不是这个特定查询的通用解决方案:有没有办法告诉 Postgres“浏览”视图并使用相同的查询计划,就像我直接输入底层 SQL 一样。
  • 发布查询执行计划?解释选择 ...
  • 我用与您的不完整陈述类似的东西对此进行了测试,但没有任何区别。从视图中选择和直接从表中选择之间的时间和执行计划相同
  • 给我们两个计划,以便我们看看是否有任何变化。

标签: performance postgresql view window-functions


【解决方案1】:

这两个计划之间的区别在于加入聚合。这可以防止使用嵌套循环计划。当您在视图中使用聚合时,您会将自己置于不利的场景中。

例如,这几乎总是会导致两个表上的合并或哈希连接计划,然后是 top-n 排序:

select foo.*
from foo
join (select bar.* from bar group by bar.field) as bar on foo.field = bar.field
where ...
order by bar.field
limit 10;

【讨论】:

    【解决方案2】:

    也许您可以考虑使用Common Table Expression (CTE) 代替视图。我可以以类似于使用视图的方式帮助使查询更清晰,但似乎不会以同样的方式影响执行计划。

    我在this question 中遇到了类似的问题,使用 CTE 代替视图可以提高执行计划的效率。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-07
      • 2021-04-21
      • 1970-01-01
      • 2021-12-18
      • 2014-06-02
      • 2022-12-19
      • 2016-12-12
      • 1970-01-01
      相关资源
      最近更新 更多