在 PostgreSQL 中为滚动总和设置非负下限

Eva*_*oll 3 postgresql aggregate window-functions running-totals

这是一个非常有趣的问题(针对 SQL Server 提出的问题),我想尝试一下,看看它是如何在 PostgreSQL 中完成的。让我们看看其他人是否可以做得更好。拿着这个数据,

CREATE TABLE foo
AS
  SELECT pkid::int, numvalue::int, groupid::int
  FROM ( VALUES
    ( 1,  -1   , 1 ),
    ( 2,  -2   , 1 ),
    ( 3,  5    , 1 ),
    ( 4,  -7   , 1 ),
    ( 5,  1    , 2 )
  ) AS t(pkid, numvalue, groupid);
Run Code Online (Sandbox Code Playgroud)

我们正在尝试生成这个:

PKID   RollingSum    GroupID
----------------------------- ## Explanation: 
1      0             1        ## 0 - 1 < 0  => 0
2      0             1        ## 0 - 2 < 0  => 0
3      5             1        ## 0 + 5 > 0  => 5
4      0             1        ## 5 - 7 < 0  => 0
Run Code Online (Sandbox Code Playgroud)

问题描述为,

当添加一个负数将导致总和为负时,将激活限制以将结果设置为零。后续的加法应该基于这个调整后的值,而不是原来的滚动总和。

应该使用加法来达到预期的结果。如果第四个数字从 -7 变为 -3,则第四个结果应该是 2 而不是 0

如果可以提供单个金额而不是几个滚动数字,那也是可以接受的。我可以使用存储过程来实现非负加法,但这太低级了。

现实生活中的问题是我们将下订单记录为正数,取消订单为负数。由于连接问题,客户可能cancel会多次单击该按钮,这将导致记录多个负值。在计算我们的收入时,“零”需要作为销售额的界限。

他们的解决方案都是使用递归。

dno*_*eth 10

这就是我使用嵌套 OLAP 函数在 Teradata 上解决类似问题的方法:

SELECT dt.*,
   -- find the lowest previous CumSum < 0       
   -- and adjust the current CumSum to zero
   Max(CASE WHEN CumSum < 0 THEN -CumSum ELSE 0 end)
       Over (PARTITION BY groupid
             ORDER BY pkid
             ROWS Unbounded Preceding)
   + CumSum AS AdjustedSum
FROM 
 ( 
   SELECT pkid, numvalue, groupid,
      -- calculate a standard cumulative sum
      Sum(numvalue)
      Over (PARTITION BY groupid
            ORDER BY pkid
            ROWS Unbounded Preceding) AS CumSum
   FROM foo
 ) AS dt
Run Code Online (Sandbox Code Playgroud)

  • @joanolo:可能比 SQL Server 的递归解决方案更有效(至少在 Teradata 上效率更高)。自定义窗口聚合函数在 Teradata 上也会更高效,但必须用 C 编码:-) (2认同)