如何基于前一行状态标记生成序列行号 替换老旧T-SQL游标方案
解法说明
这个需求属于典型的孤岛(gaps and islands)分类场景,用T-SQL原生窗口函数即可实现,完全替换老旧游标方案,批量执行性能远高于逐行遍历的游标逻辑。
实现逻辑
- 分组标记:按ID分区、日期排序,每遇到STATUS为
START的行就累加分组编号,同一个编号下的行就属于同一个连续序列 - 组内排序:在每个序列分组内按日期生成行号,直接作为A列的填充值
- 列值适配:判断当前序列组内是否存在带B后缀的延续状态,存在则B列同步用组内行号填充,否则B列保持初始值1
完整代码
假设你的源表名为YourDataTable,可直接运行以下查询得到目标结果:
WITH GroupedRecords AS ( SELECT ID, STATUS, DATE, -- 生成序列分组ID,每个START开启新分组 SUM(CASE WHEN STATUS = 'START' THEN 1 ELSE 0 END) OVER( PARTITION BY ID ORDER BY DATE ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) AS GroupID FROM YourDataTable ), RankedRecords AS ( SELECT ID, STATUS, DATE, GroupID, -- 组内按日期排序生成行号,对应A列值 ROW_NUMBER() OVER( PARTITION BY ID, GroupID ORDER BY DATE ) AS GroupRowNum, -- 判断当前组是否需要累加B列 CASE WHEN EXISTS( SELECT 1 FROM GroupedRecords g2 WHERE g2.ID = g1.ID AND g2.GroupID = g1.GroupID AND g2.STATUS LIKE '%B%' ) THEN 1 ELSE 0 END AS NeedIncreaseB FROM GroupedRecords g1 ) SELECT ID, STATUS, DATE, GroupRowNum AS A, CASE WHEN NeedIncreaseB = 1 THEN GroupRowNum ELSE 1 END AS B FROM RankedRecords ORDER BY ID, DATE;
结果验证
运行上述代码后输出的结果和你提供的目标格式完全一致,不需要额外的游标或自定义函数实现。
内容的提问来源于stack exchange,提问作者CClarke
相关产品推荐
相关产品推荐

