我正在使用Postgres 9.2。
我有以下问题:
Time | Value | Device -- Sum should be
1 v1 1 v1
2 v2 2 v1 + v2
3 v3 3 v1 + v2 + v3
4 v4 2 v1 + v4 + v3
5 v5 2 v1 + v5 + v3
6 v6 1 v6 + v5 + v3
7 v7 3 v6 + v5 + v3
基本上,总和需要跨越最近的值 及时为N个设备中的每一个。在上面的例子中,那里 是3个设备。
我尝试了几种使用窗口函数的方法 没有成功。我写了一个存储过程 做我需要的,但它是缓慢的。 SLOWness可能是我的 缺乏plpgsql的经验。
CREATE OR REPLACE FUNCTION timeseries.combine_series(id int[], startTime timestamp, endTime timestamp)
RETURNS setof RECORD AS $$
DECLARE
retval double precision = 0;
row_data timeseries.total_active_energy%ROWTYPE;
maxCount integer = 0;
sz integer = 0;
lastVal double precision[];
v_rec RECORD;
BEGIN
SELECT INTO sz array_length($1,1);
FOR row_data IN SELECT * FROM timeseries.total_active_energy WHERE time >= startTime AND time < endTime AND device_id = ANY($1) ORDER BY time
LOOP
retval = row_data.active_power;
for i IN 1..sz LOOP
IF $1[i]=row_data.device_id THEN
lastVal[i] = row_data.active_power;
ELSE
retval = retVal + COALESCE(lastVal[i],0);
END IF;
END LOOP;
SELECT row_data.time, retval into v_rec;
return next v_rec;
END LOOP;
return ;
END;
$$ LANGUAGE plpgsql;
呼叫:
select * from timeseries.combine_series('{552,553,554}'::int[], '2013-05-01'::timestamp, '2013-05-02'::timestamp)
AS (t timestamp with time zone, val double precision);
示例数据
CREATE OR REPLACE TEMP TABLE t (ts int, active_power real, device_id int, should_be int);
INSERT INTO t VALUES
(1,2,554,2)
,(2,3,553,5)
,(3,9,553,11)
,(4,7,553,9)
,(5,6,552,15)
,(6,8,554,21)
,(7,5,553,19)
,(8,7,553,21)
,(9,6,552,21)
,(10,7,552,22)
;
答案 0 :(得分:2)
我正在回答你之前的问题,在那里你提出了一个更简单的案例。阅读那里解释解决方案的窗口功能方面:
Sum across partitions with window functions
这个问题提出了一个“反交叉表”的数据集。要到达您想要的位置,您可以先运行交叉列表,将案例简化为更简单的先前格式。
PostgreSQL有额外的模块tablefunc为它提供非常快速的功能。每个数据库运行此命令一次以安装:
CREATE EXTENSION tablefunc;
然后你需要的就是这个(包括调试结果中的冗余列):
SELECT ts, active_power, device_id, should_be
, COALESCE(max(a) OVER (PARTITION BY grp_a), 0)
+ COALESCE(max(b) OVER (PARTITION BY grp_b), 0)
+ COALESCE(max(c) OVER (PARTITION BY grp_c), 0) AS special_sum
FROM (
SELECT *
,count(a) OVER w AS grp_a
,count(b) OVER w AS grp_b
,count(c) OVER w AS grp_c
FROM crosstab(
'SELECT ts, active_power, device_id, should_be
,device_id, active_power
FROM t
ORDER BY 1,2'
,'VALUES (552), (553), (554)'
) AS t (ts int, active_power int, device_id int, should_be int
,a int, b int, c int)
WINDOW w AS (ORDER BY ts)
) sub
ORDER BY ts;
返回所需的结果。
在这个查询中组装了相当多的炸药,它应该表现良好
请注意,此解决方案基于一个小的设备列表 - 您的示例中为(552, 553, 554)
。
关于crosstab()
的基础知识:
PostgreSQL Crosstab Query
关于额外列:
Pivot on Multiple Columns using Tablefunc
高级交叉表-foo:
Dynamic alternative to pivot with CASE and GROUP BY
答案 1 :(得分:0)
如果您知道“N”的值,则以下方法有效。它计算每个设备的最大time
值,然后连接回原始记录,并使用聚合对它们求和:
select tae.time, tae.value, taw.device,
SUM(coalesce(dev.value)) as sumvalue
from (select t.*,
MAX(case when device = 1 then time end) over (order by time) as dev1time,
MAX(case when device = 2 then time end) over (order by time) as dev2time,
MAX(case when device = 3 then time end) over (order by time) as dev3time
from timeseries.total_active_energy tae
) tae left outer join
timeseries.total_active_energy dev
on dev.time in (dev1time, dev2time, dev3time)
group by tae.time, taw.value, tae.device;