滑动1小时周期聚合查询

时间:2015-01-13 16:45:13

标签: sql postgresql aggregate postgresql-9.2 recursive-query

在Postgres 9.2中,我有一个表格,其中包含在特定时间点采取的措施:

CREATE TABLE measures (dt timestamptz, measure integer);

INSERT INTO measures VALUES
('2015-01-13 12:05', 10), 
('2015-01-13 12:30', 8), 
('2015-01-13 13:02', 16), 
('2015-01-13 13:30', 12), 
('2015-01-13 14:15', 7);

我想计算1小时时段的平均值和行数,我可以这样做:

SELECT date_trunc('hour', dt) as d, max(measure), count(*)
FROM measures group by d order by d;

但是,而不是从12:00,13:00等开始的1小时时段。我希望在事件发生后持续1小时。在这种情况下,这是从 12:05到13:05 的一个时段,从 13:30到14:30 的下一个时段。

这在PostgreSQL中是否可行?

2 个答案:

答案 0 :(得分:2)

recursive CTE的普通SQL有效:

WITH RECURSIVE cte AS (
   SELECT t.dt + interval '1h' AS dt, m.measure
   FROM  (SELECT dt FROM measures ORDER BY 1 LIMIT 1) t -- no lower bound
   JOIN   measures m ON m.dt < t.dt + interval '1h'  -- excl. upper bound

   UNION ALL
   SELECT t.dt + interval '1h' AS dt, m.measure
   FROM  (
      SELECT m.dt
      FROM  (SELECT dt FROM cte LIMIT 1) c
      JOIN   measures m ON m.dt > c.dt
      ORDER  BY 1
      LIMIT  1
      ) t
   JOIN   measures m ON m.dt >= t.dt                 -- incl. lower bound
                    AND m.dt <  t.dt + interval '1h' -- excl. upper bound
   )
SELECT dt AS hour_before_dt
     , round(avg(measure), 2) AS avg_measure, count(*) AS ct
FROM   cte
GROUP  BY 1
ORDER  BY 1;

返回:

dt                  | avg_measure | ct
--------------------+-------------+----
2015-01-13 13:05:00 | 11.33       | 3
2015-01-13 14:30:00 | 9.50        | 2

SQL Fiddle 在带有索引和所选时间范围的大表上进行额外测试

这甚至可以正确,索引在dt - 或更好multicolumn index,以便在Postgres 9.2 +中允许index-only scans

CREATE INDEX measures_foo_idx ON measures (dt, measure);

除了LIMIT之外,这是所有标准SQL(including the recursive CTE本身)。 Postgres也支持标准关键字FETCH FIRST,如果您需要所有标准SQL。

为什么不能使用单一窗口功能?

虽然窗口函数的结果是窗口框架上的聚合,但框架定义本身不能引用其他行。在您的情况下,通过从头到尾考虑所有行来动态确定粒度。单个窗口功能无法实现这一点。

优化性能

您基本上需要迭代所有行,这可以通过程序解决方案更快:plpgsql函数中的 FOR循环。哪个会更快?

  • 几小时的递归查询多行
  • 多个小时的功能每行

相关的plpgsql解决方案:

答案 1 :(得分:0)

如果你能找到一个函数postgresql,它会在一个日期时间增加一个小时,那么你应该能够根据内部查询中的日期和日期+ 1小时加入你自己的结果集,然后在一个日期和时间内将值加起来。外部查询以获得您需要的结果。

SELECT
    LowDate,
    HighDate=DATEADD(HOUR,1,LowDate),
    SumMeasure=SUM(measure),
    ItemCount=COUNT(*)
FROM
(
    SELECT
        LowDate=M1.dt,  
        measure=M2.measure
    FROM
        measures M1 
        INNER JOIN measures M2 ON M2.dt BETWEEN M1.dt AND DATEADD(HOUR,1,M1.dt)
)AS DETAIL  
GROUP BY
    LowDate 
ORDER BY
    LowDate