纯SQL实现:统计最后一个0之后的1的数量及Amount列求和(高效查询)
解决方案:用窗口函数实现单次扫描的高效统计
这是个很常见的业务统计需求,而且要求仅访问一次数据保证效率,我们可以利用窗口函数来精准定位最后一个flag=0的位置,再筛选后续符合条件的记录完成统计,全程只需要扫描一次表。
核心SQL代码
SELECT COUNT(*) AS count_1, SUM(amount) AS sum_amount FROM ( SELECT flag, amount, -- 标记当前记录是否处于最后一个0之后的区间 CASE WHEN MAX(CASE WHEN flag = 0 THEN id END) OVER () < id THEN 1 ELSE 0 END AS is_after_last_0 FROM your_table ) t WHERE is_after_last_0 = 1 AND flag = 1 -- 若最后一条记录是0,或最后一个0之后没有1,结果会返回空,符合需求
代码细节解释
子查询逻辑:
MAX(CASE WHEN flag = 0 THEN id END) OVER ():通过窗口函数MAX()结合全局窗口OVER(),计算出整个表中最后一条flag=0记录的主键id(如果你的表是按时间排序,把id换成时间字段比如create_time即可)。- 用
CASE判断当前记录的id是否大于这个最后一个0的id,是则标记为1,代表这条记录在最后一个0之后。
外层统计逻辑:
- 筛选出
is_after_last_0=1且flag=1的记录,直接统计数量和金额总和。 - 如果最后一条记录本身就是
flag=0,那么MAX(CASE WHEN flag=0 THEN id END)就是最后一条记录的id,此时没有记录的id会大于它,外层查询自然返回空结果,完美符合需求。
- 筛选出
适配时间排序场景
如果你的数据是按时间顺序而非主键排序,只需要替换排序字段即可:
SELECT COUNT(*) AS count_1, SUM(amount) AS sum_amount FROM ( SELECT flag, amount, CASE WHEN MAX(CASE WHEN flag = 0 THEN create_time END) OVER () < create_time THEN 1 ELSE 0 END AS is_after_last_0 FROM your_table ) t WHERE is_after_last_0 = 1 AND flag = 1
验证示例数据
用你给出的示例数据测试:
| Flag | Amount | id |
|---|---|---|
| 0 | 5 | 1 |
| 1 | 8 | 2 |
| 0 | 10 | 3 |
| 1 | 20 | 4 |
| 1 | 30 | 5 |
子查询中MAX(CASE WHEN flag=0 THEN id END)的结果是3,所以id=4和id=5的is_after_last_0为1,且它们的flag=1,外层统计后得到count_1=2,sum_amount=50,和预期完全一致。
如果最后一条记录是0,比如:
| Flag | Amount | id |
|---|---|---|
| 0 | 5 | 1 |
| 1 | 8 | 2 |
| 0 | 10 | 3 |
此时MAX(CASE WHEN flag=0 THEN id END)是3,没有记录的id大于3,外层查询返回空结果,符合要求。
内容的提问来源于stack exchange,提问作者gipsy
相关产品推荐
相关产品推荐

