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。

如果您还有其他问题,请告诉我。谢谢!

4

1 回答 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;

请记住,自动分析不处理外部表;你必须自己处理。

如果这不起作用,您可以尝试在外表上设置use_remote_estimate选项,并确保远程数据库上的表统计信息准确。

于 2020-01-28T07:15:10.567 回答