【问题标题】:Sum across partitions with window functions使用窗口函数跨分区求和
【发布时间】:2013-06-17 09:31:39
【问题描述】:

我有以下问题...

    Time |  A   |  B |  C  --  Sum should be
      1     a1     b1   c1     a1 + b1 + c1
      2     a2     b2   x      a2 + b1 + c1 
      3     a3     x    x      a3 + b2 + c1
      4     x      b3   c2     a3 + b3 + c2

基本上,总和需要跨越最近的值 及时为三行中的每一行。每个数据列不 必须具有当前时间的值。

我尝试了几种使用窗口函数的方法,并且 一直没有成功。我写了一个存储过程 做我需要的,但它很慢。

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);

【问题讨论】:

  • 我认为你的第二行应该是 a2 + b2 + c1?
  • 查询应该返回总和。总和总是在运行中完成。
  • 您的新问题应该有解决方案。但我要求你先遵守这里的风俗习惯。创建一个新问题,在给出答案后不要改变现有问题的性质。我已经回滚了你有问题的编辑。您可以在版本历史记录中查看您的更改。另外,在做的时候:一个测试用例应该提供简单的数据。您的新测试用例不必要地复杂,这无助于提出问题。请添加您的 Postgres 版本。
  • 正在添加新问题...谢谢
  • 我想我有一个有趣的解决方案...

标签: sql postgresql stored-procedures plpgsql window-functions


【解决方案1】:
SELECT ts, a, b, c
       , 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   t
   WINDOW w AS (ORDER BY ts)
   ) sub
ORDER  BY ts;

首先,使用聚合窗口函数count() 将实际值和后面的NULL 值放在一个组中:它不会随NULL 值递增。

然后从每个组中获取max(),到达您要查找的内容。此时您也可以使用min()sum(),因为每个组只有一个非空值。

如果整个时间的第一个值是NULLCOALESCE() 捕获 NULL 值。

请注意我是如何选择 ts 作为列名的,因为我不使用像 time 这样的基本类型名称作为标识符。

测试用例

这也是你们每个人首先应该提供示例数据的方式!

CREATE TEMP TABLE t (ts int, a int, b int, c int);

INSERT INTO t VALUES
  (1, 11,   21,   NULL)
 ,(2, 12,   22,   NULL)
 ,(3, 13,   NULL, NULL)
 ,(4, NULL, 23,   32);

【讨论】:

  • 抱歉没有提供数据,现在我意识到我没有正确说明问题。再次谢谢你。编辑
  • @user2505340:我很欣赏您尝试正确回答问题的方式。但是,在给出答案后改变问题的性质通常是不被接受的。相反,创建一个新的不同问题并将更改回滚到这个问题。
猜你喜欢
  • 1970-01-01
  • 2016-06-01
  • 1970-01-01
  • 2023-03-06
  • 1970-01-01
  • 2014-04-26
  • 2018-09-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多