累计余额列计算异常:SQL窗口函数语法咨询
我的SQL查询中,cum_balance列用于计算累计余额,但第10行后出现异常——从该行起hour列值相同,累计值不再更新。原查询语句如下:
select hour, symbol, amount_usd, category, sum(amount_usd) over ( order by hour asc RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) as cum_balance from combined_transfers_usd_netflow order by hour
我尝试过移除RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW、添加partition by hour和group by hour,均未得到预期结果,也未报错。查询结果如下:
| 行号 | Hour | SYMBOL | AMOUNT_USD | CATEGORY | CUM_BALANCE |
|---|---|---|---|---|---|
| 1 | 2021-12-02 23:00:00 | WETH | 227.2795 | in | 227.2795 |
| 2 | 2021-12-03 00:00:00 | WETH | -226.4801153 | out | 0.7993847087 |
| 3 | 2022-01-05 21:00:00 | WETH | 5123.716203 | in | 5124.515587 |
| 4 | 2022-01-18 14:00:00 | WETH | -4466.2366 | out | 658.2789873 |
| 5 | 2022-01-19 00:00:00 | WETH | 2442.618599 | in | 3100.897586 |
| 6 | 2022-01-21 14:00:00 | USDC | 99928.68644 | in | 103029.584 |
| 7 | 2022-03-01 16:00:00 | UNI | 8545.36098 | in | 111574.945 |
| 8 | 2022-03-04 22:00:00 | USDC | -2999.343 | out | 108575.602 |
| 9 | 2022-03-09 22:00:00 | USDC | -5042.947675 | out | 103532.6543 |
| 10 | 2022-03-16 21:00:00 | USDC | -4110.6579 | out | 98594.35101 |
| 11 | 2022-03-16 21:00:00 | UNI | -3.209306045 | out | 98594.35101 |
| 12 | 2022-03-16 21:00:00 | UNI | -16.04653022 | out | 98594.35101 |
| 13 | 2022-03-16 21:00:00 | UNI | -16.04653022 | out | 98594.35101 |
| 14 | 2022-03-16 21:00:00 | UNI | -16.04653022 | out | 98594.35101 |
| 15 | 2022-03-16 21:00:00 | UNI | -6.418612089 | out | 98594.35101 |
问题根源
问题出在窗口框架的RANGE关键字上。当order by的列值重复时(比如第10到15行的hour完全相同),RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW会将所有hour值相同的行纳入“当前行范围”,导致这些行的累计余额都会显示为包含所有同hour行的总和,无法实现逐行累加。
你之前的尝试方向不对:移除框架声明后,部分数据库(如PostgreSQL)默认会对单个排序列使用RANGE框架,所以问题依旧;添加partition by hour会把每个hour的数据单独分区,累计值会在每个hour内重置,不符合需求;group by hour会合并同hour的行,丢失明细数据,也不是你要的结果。
正确的SQL语句
只需将RANGE替换为ROWS,强制窗口按物理行范围计算,就能实现逐行累加:
select hour, symbol, amount_usd, category, sum(amount_usd) over ( order by hour asc ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) as cum_balance from combined_transfers_usd_netflow order by hour
如果你的数据库支持,也可以简化写法——因为ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW是order by搭配ROWS框架时的默认值,所以可以省略框架声明:
select hour, symbol, amount_usd, category, sum(amount_usd) over (order by hour asc) as cum_balance from combined_transfers_usd_netflow order by hour
注意:若数据库默认框架是RANGE(如PostgreSQL),必须显式指定ROWS才能确保逐行累加。
内容的提问来源于stack exchange,提问作者Vaishak J

