【问题标题】:Postgres - Fast way to sum over rows from last day of monthPostgres - 从每月最后一天总结行的快速方法
【发布时间】:2016-11-09 18:37:06
【问题描述】:

我想查询一个表并为该月最后一天的所有行求和一列。

我们以下表为例:

CREATE TABLE example(dt date, value int)

(真实表列多,比较大,真实查询比较复杂)

我有以下疑问:

SELECT dt, SUM(value)
FROM example
WHERE dt IN  (SELECT DISTINCT
     date_trunc('MONTH', generate_series('2012-01-01'::date,
                                         '2016-12-01'::date,
                                         interval '1 day') + INTERVAL '1 MONTH - 1 day')::date)
GROUP BY dt

它在我的真实桌子上运行大约 2 秒。

但是,如果我在我的范围内生成月末日期的完整列表并将查询参数化,如下所示:

SELECT dt, SUM(value)
FROM example
WHERE dt IN ('2012-01-31', ...)
GROUP BY dt

要快得多,大约 750 毫秒。

我不希望生成日期并将它们传递给这样的查询,有没有一种方法可以完全在 SQL 中完成并使其与后一个版本一样快?

【问题讨论】:

    标签: sql performance postgresql date


    【解决方案1】:

    子选择是不必要的复杂。可以简化为:

    SELECT dt, SUM(value)
    FROM example
    WHERE dt IN (SELECT d::date
                 from generate_series('2012-01-01'::date, '2016-12-01'::date, interval '1 month') dates (d)
    GROUP BY dt;  --<< the group by is necessary
    

    也许这会加快查询速度。

    您也可以尝试将日期生成放入 CTE:

    with dates (d) as (
      SELECT t::date
      from generate_series('2012-01-01'::date, '2016-12-01'::date, interval '1 month') t
    )
    SELECT dt, SUM(value)
    FROM example
    WHERE dt IN ( select d from dates)
    GROUP BY dt;
    

    有时做 JOIN 也更有效率:

    with dates (d) as (
      SELECT t::date
      from generate_series('2012-01-01'::date, '2016-12-01'::date, interval '1 month') t
    )
    SELECT dt, SUM(value)
    FROM example
      JOIN dates on example.dt = dates.d
    GROUP BY dt;
    

    【讨论】:

    • 这是我实际使用的查询:SELECT dt, SUM(value) FROM example WHERE dt in (select date_trunc('month', d) + interval '1 month - 1 day' from generate_series('2012-01-01'::date, '2016-11-10'::date, interval '1 month') dates(d))
    【解决方案2】:

    查询中的性能问题来自于您正在生成每日系列这一事实。将其更改为每月,删除 distinct 并添加 group by

    select dt, sum(value)
    from
        example
        inner join (
           select date_trunc('month', dt) + interval '1 month - 1 day' as dt
           from generate_series('2012-01-01'::date, '2016-12-01', '1 month') gs (dt)
        ) d using (dt)
    group by dt
    

    【讨论】:

      猜你喜欢
      • 2017-05-14
      • 2020-08-25
      • 2019-03-04
      • 2015-02-06
      • 2018-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多