我有一个PostgreSQL 9.1数据库,其中包含一个包含时间戳和一个值的表
'2012-10-25 01:00' 2
'2012-10-25 02:00' 5
'2012-10-25 03:00' 12
'2012-10-25 04:00' 7
'2012-10-25 05:00' 1
... ...
我需要在每小时8小时的范围内平均值。换句话说,我需要平均1h-8h,2h-9h,3h-10h等。
我不知道如何进行此类查询。我到处寻找,但也不知道要寻找什么功能。
我发现的关闭是每小时/每日平均值或平均值(例如1小时-8小时,9小时-16小时等)。但在这些情况下,时间戳只是使用date_trunc()
函数进行转换(如下例所示),这对我没用。
我认为我正在寻找的是与此类似的功能
SELECT date_trunc('day', timestamp), max(value)
FROM table_name
GROUP BY date_trunc('day', timestamp);
然后在group-by子句中每小时使用某种8小时的范围。这甚至可能吗?
答案 0 :(得分:8)
window function with a custom frame使非常简单:
SELECT ts
,avg(val) OVER (ORDER BY ts
ROWS BETWEEN CURRENT ROW AND 7 FOLLOWING) AS avg_8h
FROM tbl;
每个平均值的帧是当前行加上以下7.这假设您每小时只有一行。您的示例数据似乎意味着,但您没有指定。
原样,avg_8h
表示最后一行(根据ts
)7行的行数用较少的行计算,直到最后一行的值等于它自己的平均值。您没有指定如何处理特殊情况。
答案 1 :(得分:1)
关键是要创建一个虚拟表来加入结果集。 generate_series
函数可以通过以下方式帮助实现此目的:
SELECT
start
, start + interval '8 hours' as end
FROM (
SELECT generate_series(
date'2012-01-01'
, date'2012-02-02'
, '1 hour'
) AS start
) x;
这会产生如下输出:
start | end
------------------------+------------------------
2012-01-01 00:00:00+00 | 2012-01-01 08:00:00+00
2012-01-01 01:00:00+00 | 2012-01-01 09:00:00+00
2012-01-01 02:00:00+00 | 2012-01-01 10:00:00+00
2012-01-01 03:00:00+00 | 2012-01-01 11:00:00+00
这可以为您提供加入数据的功能。这样,以下查询:
SELECT
y.start
, round(avg(ts_val.v))
FROM
ts_val,
(
SELECT
start
, start + interval '8 hours' as end
FROM (
SELECT generate_series(
date'2012-01-01'
, date'2012-02-02'
, '1 hour'
) AS start
) x
) y
WHERE
ts BETWEEN y.start AND y.end
GROUP BY
y.start
ORDER BY
y.start
;
以下数据
ts | v
---------------------+---
2012-01-01 01:00:00 | 2
2012-01-01 09:00:00 | 2
2012-01-01 10:00:00 | 5
(3 rows)
将产生以下结果:
start | round
------------------------+-------
2012-01-01 00:00:00+00 | 2.0
2012-01-01 01:00:00+00 | 2.0
2012-01-01 02:00:00+00 | 3.5
2012-01-01 03:00:00+00 | 3.5
2012-01-01 04:00:00+00 | 3.5
2012-01-01 05:00:00+00 | 3.5
2012-01-01 06:00:00+00 | 3.5
2012-01-01 07:00:00+00 | 3.5
2012-01-01 08:00:00+00 | 3.5
2012-01-01 09:00:00+00 | 3.5
2012-01-01 10:00:00+00 | 5.0
(11 rows)