Postgresql
在 PostgreSQL 中為滾動總和設置非負下限
這是一個非常有趣的問題(詢問 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);
我們正在嘗試生成這個:
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
問題描述為,
當添加負數將導致和為負時,將啟動限制以將結果設置為零。後續的加法應該基於這個調整後的值,而不是原來的滾動和。
使用加法應該達到預期的結果。如果第四個數字從 -7 變為 -3,則第四個結果應該是 2 而不是 0
如果可以提供一個總和而不是幾個滾動數字,那也是可以接受的。我可以使用儲存過程來實現非負加法,但這太低級了。
現實生活中的問題是我們將下訂單記錄為正數,將取消記錄為負數。由於連接問題,客戶可能會多次點擊該
cancel
按鈕,這將導致記錄多個負值。在計算我們的收入時,“零”需要作為銷售的邊界。他們的解決方案都是使用遞歸。
這就是我使用嵌套 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
使用自定義聚合函式
我們
CREATE FUNCTION
用來創建一個int_add_pos_or_zero
添加數字的函式,但如果它們小於 0,則返回 0。CREATE FUNCTION int_add_pos_or_zero(int, int) RETURNS int AS $$ BEGIN RETURN greatest($1 + $2, 0); END; $$ LANGUAGE plpgsql IMMUTABLE;
現在我們
CREATE AGGREGATE
就可以在視窗函式中執行它了。我們設置INITCOND
為=0
.CREATE AGGREGATE add_pos_or_zero(int) ( SFUNC = int_add_pos_or_zero, STYPE = int, INITCOND = 0 );
現在我們像任何其他視窗函式一樣查詢它。
SELECT pkid, groupid, numvalue, add_pos_or_zero(numvalue) OVER (PARTITION BY groupid ORDER BY pkid) FROM foo; pkid | groupid | numvalue | add_pos_or_zero ------+---------+----------+----------------- 1 | 1 | -1 | 0 2 | 1 | -2 | 0 3 | 1 | 5 | 5 4 | 1 | -7 | 0 5 | 2 | 1 | 1 (5 rows)