2013-06-20 51 views
1

我正在使用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) 
; 

回答

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

高級交叉-富:
​​

+0

非常感謝。表現非常好。 – user2505340

+0

我必須說,tablefunc功能是非常有用的。我希望我早些時候知道這件事。 – user2505340

+0

@ user2505340:'crosstab()'處理複雜的邏輯,這對一些人來說是令人困惑的。但是一旦你可以圍繞它思考,你就可以獲得快速和緊湊的查詢。如果你有更多的用例 - 我在過去一年左右發佈了[相當多的綜合答案](http://stackoverflow.com/search?q=user%3A939860+ [crosstab])。 –

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; 
相關問題