PostgreSQL:汇总持续时间列表中的每日总和

时间:2016-10-08 14:23:03

标签: sql postgresql subquery aggregate

现状

我有一个跟踪某个资源使用情况的表。它看起来像这样:

started   | timestamp with time zone | not null
last_ping | timestamp with time zone |
stopped   | timestamp with time zone |

stoppedlast_ping字段可能为空。如果它们都已填满,则stopped是相关的。

开始和结束日期只能跨越几秒或多个日期。

目标

我想获得过去14天的每日使用情况清单。

当前状态

我知道我可以获得过去14天的清单:

SELECT day
FROM generate_series(CURRENT_DATE, CURRENT_DATE - 14, '-1 day'::interval) day;

我可以获得每个使用条目的总持续时间:

SELECT COALESCE(stopped, last_ping, started) - started AS duration
FROM api_sessionusage;

我还可以将这两个查询组合在一起,并添加一个限制,该限制仅考虑到午夜的持续时间:

SELECT
  day,
  (
    SELECT SUM(
      LEAST(COALESCE(stopped, last_ping, started), day + interval '1 day') - started
    )
    FROM api_sessionusage
    WHERE started >= day AND started < day + interval '1 day'
  ) AS aggregated_duration
FROM generate_series(CURRENT_DATE, CURRENT_DATE -14, '-1 day'::interval) day;

这里的问题是,午夜后结束的使用会话仅计入开始日期,但午夜后的持续时间不会被考虑在内。

如何重写查询以便在过去14天内获得每天的汇总使用情况?

1 个答案:

答案 0 :(得分:3)

使用overlap and intersect operators时间戳范围类型:

select 
    day, 
    sum(upper(daily_range) - lower(daily_range))
from (
    select 
        day, 
        session_range * tstzrange(day, day::date + 1) daily_range
    from generate_series(current_date, current_date -14, '-1 day'::interval) day
    left join (
        select tstzrange(started, coalesce(stopped, last_ping, started)) session_range
        from api_sessionusage
        ) s
    on session_range && tstzrange(day, day::date + 1)
) s
group by 1
order by 1;

注意:

coalesce(stopped, last_ping, started) - started as duration
如果stoppedlast_ping都为空,则

为零。也许它应该是

coalesce(stopped, last_ping, current_date) --?