PostgresSQL嵌套循环-计划者在进行INNER JOIN时何时决定使用嵌套循环?

问题描述 投票:1回答:1

我正在使用INNER JOIN运行查询,计划者决定使用嵌套循环。我已经发现它与WHERE条件有关,因为我尝试使用不同的WHERE条件编写查询,因此它返回相同的结果,但不使用嵌套循环。

我的问题是,当查询看起来相同时,为什么它们都返回相同的结果,所以计划者为什么要做出不同的决定?该查询在使用嵌套循环的情况下运行的时间为77秒,在没有嵌套循环的情况下的运行时间为13秒,而在13秒的条件下运行的查询非常丑陋且不雅致,使我认为有一种更好的编写方法。

这里是两个查询。请注意,两者之间的区别在于WHERE子句如何按日期过滤,其中第一个使用BETWEEN,第二个使用一系列OR语句。我知道current_date包装在自己的子查询中很奇怪,但这是因为这些查询使用的是外部数据包装器。这允许将current_date作为不可变对象传递,以大大提高性能。

SELECT ROUND(AVG(m.forecast - w.wind),6) from pjm.wind_forecast_recent w
    INNER JOIN pjm.load_forecast_recent m ON w.pricedate = m.pricedate AND w.hour = m.hour
  WHERE w.hour = 5 AND m.area = 'RTO_COMBINED' AND 
            (w.pricedate BETWEEN (SELECT current_date-6) AND (SELECT current_date));

-----------

SELECT ROUND(AVG(m.forecast - w.wind),6) from pjm.wind_forecast_recent w
    INNER JOIN pjm.load_forecast_recent m ON w.pricedate = m.pricedate AND w.hour = m.hour
  WHERE w.hour = 5 AND m.area = 'RTO_COMBINED' AND (
    w.pricedate = (SELECT current_date-6) OR
    w.pricedate = (SELECT current_date-5) OR
    w.pricedate = (SELECT current_date-4) OR
    w.pricedate = (SELECT current_date-3) OR
    w.pricedate = (SELECT current_date-2) OR
    w.pricedate = (SELECT current_date-1) OR
    w.pricedate = (SELECT current_date))

这是各自的解释分析:

Aggregate  (cost=842341.01..842341.02 rows=1 width=32) (actual time=77120.088..77120.089 rows=1 loops=1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.007..0.008 rows=1 loops=1)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  ->  Nested Loop  (cost=840333.25..842340.97 rows=1 width=18) (actual time=14719.661..77119.994 rows=7 loops=1)
        ->  Foreign Scan on wind_forecast_recent w  (cost=242218.45..242218.49 rows=1 width=18) (actual time=3184.714..3184.720 rows=7 loops=1)
        ->  Foreign Scan on load_forecast_recent m  (cost=598114.80..600122.47 rows=1 width=16) (actual time=10531.723..10531.724 rows=1 loops=7)
Planning Time: 744.979 ms
Execution Time: 77227.512 ms
Aggregate  (cost=841657.94..841657.95 rows=1 width=32) (actual time=13683.022..13683.023 rows=1 loops=1)
  InitPlan 1 (returns $0)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.006..0.006 rows=1 loops=1)
  InitPlan 2 (returns $1)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 3 (returns $2)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 4 (returns $3)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 5 (returns $4)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 6 (returns $5)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  InitPlan 7 (returns $6)
    ->  Result  (cost=0.00..0.01 rows=1 width=4) (actual time=0.001..0.001 rows=1 loops=1)
  ->  Foreign Scan  (cost=833725.15..841657.83 rows=1 width=18) (actual time=13682.974..13682.977 rows=7 loops=1)
        Relations: (pjm.wind_forecast_recent w) INNER JOIN (pjm.load_forecast_recent m)
Planning Time: 332.870 ms
JIT:
  Functions: 16
  Options: Inlining true, Optimization true, Expressions true, Deforming true
  Timing: Generation 4.163 ms, Inlining 15.088 ms, Optimization 44.489 ms, Emission 28.064 ms, Total 91.804 ms
Execution Time: 13724.094 ms

我正在Ubuntu 18.04服务器上运行PostgreSQL 12.1。

让我知道您是否还有其他问题。谢谢!

sql postgresql query-performance sql-execution-plan postgres-fdw
1个回答
2
投票

计划者并没有基于深度推理来决定使用某种联接策略,它只是构建所有可能的联接策略,估算成本并选择最便宜的联接策略。

就是说,如果外部表很小,则嵌套循环联接通常是最佳选择,这样就不必经常执行内部循环。另外,关于内部表的连接条件的索引可以大大降低嵌套循环连接的成本,并使之成为有吸引力的策略。

在您的情况下,错误的选择是由于估计错误:

Foreign Scan on wind_forecast_recent w  (cost=... rows=1 ...) (actual ... rows=7 ...)

这将使内部循环执行7次而不是一次,因此执行时间是70秒而不是10秒。

您应该在wind_forecast_recent上收集表格统计信息:

ANALYZE wind_forecast_recent;

[请记住,自动分析不会not处理外部表;您必须自己照顾一下。

如果不能解决问题,您可以尝试在外部表上设置use_remote_estimate选项,并确保在远程数据库上表统计信息是正确的。

© www.soinside.com 2019 - 2024. All rights reserved.