在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中是否可行?
答案 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