基于单调递增行检查线程,需通过行偏移量实现计数器累加值
解决方案:计算带重置的计数器累加值
首先得明确需求核心:咱们有个计数器列val,遇到异常会重置为0,现在要新增一列记录从开始到当前行的所有有效计数累加总和,同时要求未发生重置的连续区间内,新列值和原val列当前值保持一致。这种带分段重置的累加需求,用窗口函数就能轻松解决,下面分步骤给你演示:
步骤1:给重置周期打分组标记
首先要把每次val=0的重置事件作为分界点,把数据分成一个个连续的计数周期。用SUM()窗口函数就能生成唯一的分组ID:
SELECT id, val, -- 每碰到一次val=0,分组ID就加1,把后续数据归到新分组 SUM(CASE WHEN val = 0 THEN 1 ELSE 0 END) OVER (ORDER BY id) AS group_id FROM your_table;
举个例子,假设原始数据是:
| id | val |
|---|---|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 0 |
| 5 | 1 |
| 6 | 2 |
| 7 | 0 |
| 8 | 4 |
运行上面的语句后会得到分组结果:
| id | val | group_id |
|---|---|---|
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 0 | 1 |
| 5 | 1 | 1 |
| 6 | 2 | 1 |
| 7 | 0 | 2 |
| 8 | 4 | 2 |
步骤2:计算全局累加值
接下来基于分组,先算出当前分组之前所有周期的总累加,再加上当前分组内的累计值,就能得到全局的累加列:
WITH grouped_data AS ( SELECT id, val, SUM(CASE WHEN val = 0 THEN 1 ELSE 0 END) OVER (ORDER BY id) AS group_id, -- 计算当前行之前所有非重置值的总和 SUM(CASE WHEN val = 0 THEN 0 ELSE val END) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS previous_total FROM your_table ) SELECT id, val, -- 重置行直接取之前的总累加;非重置行则在之前总和基础上加当前分组的累计 CASE WHEN val = 0 THEN COALESCE(previous_total, 0) ELSE COALESCE(previous_total, 0) + SUM(val) OVER (PARTITION BY group_id ORDER BY id) END AS total_accum FROM grouped_data;
最终得到的结果完全符合需求:
| id | val | total_accum |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 2 | 2 |
| 3 | 3 | 3 |
| 4 | 0 | 3 |
| 5 | 1 | 4 |
| 6 | 2 | 6 |
| 7 | 0 | 6 |
| 8 | 4 | 10 |
额外说明
- 必须保证数据有可靠的排序字段(比如
id、时间戳),否则分组和累计逻辑会出错; - 如果只需要每个周期内的局部累计(不需要全局累加),可以简化成只计算分组内的累计值;
- 不同SQL方言的窗口函数语法略有差异,但MySQL 8.0+、PostgreSQL、SQL Server都支持上述写法,老版本MySQL可以用变量实现类似逻辑。
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

