左连接会导致查询解决时间大幅增加

时间:2017-09-20 17:35:43

标签: sql postgresql postgresql-9.6

以下查询帮助我计算偶数时间间隔内分布的历史值的平均值。

EXPLAIN ANALYZE SELECT start_date as date, AVG(hcv1.value::float) as value
FROM generate_series(cast('2017-01-01' as abstime), cast('2017-12-01' as abstime), interval '86400 seconds') start_date
LEFT JOIN history_values hv
ON (
    hv.variable_id = 3 AND 
    hv.created_at BETWEEN start_date AND start_date + interval '86400 seconds'
)
GROUP BY start_date 
ORDER BY start_date

此处是查询报告:https://explain.depesz.com/s/q29a

现在,如果我尝试添加一个指向另一个variable_id的额外列值2,则查询时间将从2秒增加到150秒:

EXPLAIN ANALYZE SELECT start_date as date,
AVG(hv1.value::float) as value1,
AVG(hv2.value::float) as value2
FROM generate_series(cast('2017-01-01' as abstime), cast('2017-12-01' as abstime), interval '86400 seconds') start_date
LEFT JOIN history_values hv1
ON (
    hv1.variable_id = 2 AND 
    hv.created_at BETWEEN start_date AND start_date + interval '86400 seconds'
)
LEFT JOIN history_values hv2
ON (
    hv2.variable_id = 3 AND 
    hv.created_at BETWEEN start_date AND start_date + interval '86400 seconds'
)
GROUP BY start_date 
ORDER BY start_date

以下是报告:https://explain.depesz.com/s/V1sV

有人可以告诉我为什么吗?我真的希望时间大约是4秒,而不是差不多75倍。

另请注意:

SELECT COUNT(*) FROM history_values WHERE variable_id = 2 -- ~25k records
SELECT COUNT(*) FROM history_values WHERE variable_id = 3 -- ~25k records

1 个答案:

答案 0 :(得分:2)

您没有添加额外的列,而是添加了另一个连接条件。而且你不需要额外的加入..

请尝试过滤avg()

EXPLAIN ANALYZE
SELECT start_date as date,
  AVG(hv1.value::float) FILTER ( WHERE hv1.variable_id = 1 ) as value1,
  AVG(hv2.value::float) FILTER ( WHERE hv1.variable_id = 2 ) as value2  
FROM generate_series(
  cast('2017-01-01' as abstime)
  , cast('2017-12-01' as abstime),
  , interval '86400 seconds'
) AS start_date
LEFT JOIN history_values hv1
ON (
  hv1.created_at >= cast('2017-01-01' as abstime) AND
  hv1.created_at <= cast('2017-12-01' as abstime) AND
  hv1.created_at >= start_date AND 
  hv1.created_at < start_date + interval '86400 seconds'
)
GROUP BY start_date 
ORDER BY start_date

作为旁注,您不应该使用abstime。这应该仅供内部使用。相反,我会使用

EXPLAIN ANALYZE
SELECT start_date::date AS date,
  AVG(hv1.value::float) FILTER ( WHERE hv1.variable_id = 1 ) as value1,
  AVG(hv2.value::float) FILTER ( WHERE hv1.variable_id = 2 ) as value2
FROM generate_series(
  timestamp with time zone '2017-01-01',
  timestamp with time zone '2017-12-01',
  interval '1 day'
) AS start_date
LEFT JOIN history_values hv1
ON (
  hv1.created_at BETWEEN (
    timestamp with time zone '2017-01-01'
    AND timestamp with time zone '2017-12-01'
  ) AND
  hv1.created_at >= start_date AND 
  hv1.created_at < start_date + interval '1 day' AND
  hv1.variable_id IN (1,2)

)
GROUP BY start_date 
ORDER BY start_date

我还认为你可以将这些范围缩小..

EXPLAIN ANALYZE
SELECT start_date::date AS date,
  AVG(hv1.value::float) FILTER ( WHERE hv1.variable_id = 1 ) as value1,
  AVG(hv2.value::float) FILTER ( WHERE hv1.variable_id = 2 ) as value2
FROM generate_series(
  timestamp with time zone '2017-01-01',
  timestamp with time zone '2017-12-01' - interval '1 day'
  interval '1 day'
) AS start_date
LEFT JOIN history_values hv1
  ON hv1.created_at BETWEEN start_date AND (start_date + interval '1 day' )
  AND hv1.variable_id IN (1,2)
GROUP BY start_date 
ORDER BY start_date

将来,请在http://dba.stackexchange.com上询问PostgreSQL特有的问题。我会在那里标记这个迁移。管理员很乐意移动它。