基于游标遍历分区数据直至满足计数器条件的实现问询
基于游标的每日调整数据处理实现方案
说明
我了解存在其他更高效的实现方案,但仍希望获取游标实现该需求的结构设计指导。
起始数据(SELECT语句返回结果)
| 日期 | ID | StartingValue | DailyAdjstVal | UnitAdjustment | EndingValue | SumOfUnitAdjustment (应等于DailyAdjstVal) |
|---|---|---|---|---|---|---|
| 2022/12/1 | d | 7 | 6 | 0 | 7 | 0 |
| 2022/12/1 | a | 5 | 6 | 0 | 5 | 0 |
| 2022/12/1 | c | 4 | 6 | 0 | 4 | 0 |
| 2022/12/1 | b | 3 | 6 | 0 | 3 | 0 |
| 2022/12/1 | e | 2 | 6 | 0 | 2 | 0 |
| 2022/12/2 | b | 8 | 4 | 0 | 8 | 0 |
| 2022/12/2 | c | 4 | 4 | 0 | 4 | 0 |
| 2022/12/2 | a | 3 | 4 | 0 | 3 | 0 |
| 2022/12/2 | d | 2 | 4 | 0 | 2 | 0 |
| 2022/12/2 | e | 2 | 4 | 0 | 2 | 0 |
| 2022/12/3 | d | 6 | 5 | 0 | 6 | 0 |
| 2022/12/3 | a | 5 | 5 | 0 | 5 | 0 |
| 2022/12/3 | c | 4 | 5 | 0 | 4 | 0 |
| 2022/12/3 | b | 3 | 5 | 0 | 3 | 0 |
| 2022/12/3 | e | 0 | 5 | 0 | 0 | 0 |
目标输出(需更新最后三列)
| 日期 | ID | StartingValue | DailyAdjstVal | UnitAdjustment | EndingValue | SumOfUnitAdjustment (应等于DailyAdjstVal) |
|---|---|---|---|---|---|---|
| 2022/12/1 | d | 7 | 6 | 3 | 4 | 6 |
| 2022/12/1 | a | 5 | 6 | 2 | 3 | 6 |
| 2022/12/1 | c | 4 | 6 | 1 | 3 | 6 |
| 2022/12/1 | b | 3 | 6 | 0 | 3 | 6 |
| 2022/12/1 | e | 2 | 6 | 0 | 2 | 6 |
| 2022/12/2 | b | 8 | 4 | 3 | 5 | 4 |
| 2022/12/2 | c | 4 | 4 | 1 | 3 | 4 |
| 2022/12/2 | a | 3 | 4 | 0 | 3 | 4 |
| 2022/12/2 | d | 2 | 4 | 0 | 2 | 4 |
| 2022/12/2 | e | 2 | 4 | 0 | 2 | 4 |
| 2022/12/3 | d | 6 | 5 | 2 | 4 | 5 |
| 2022/12/3 | a | 5 | 5 | 2 | 3 | 5 |
| 2022/12/3 | c | 4 | 5 | 1 | 3 | 5 |
| 2022/12/3 | b | 3 | 5 | 0 | 3 | 5 |
| 2022/12/3 | e | 0 | 5 | 0 | 0 | 5 |
核心操作逻辑
- 游标按日期分区处理,完成一个日期的所有数据后再处理下一个日期
- 每个日期内的数据按
StartingValue降序排序 - 初始化
@Counter = 0 - 从该日期下
StartingValue最高的行开始遍历:若EndingValue > 3,则UnitAdjustment +=1、EndingValue -=1、@Counter +=1 - 若
@Counter < DailyAdjstVal,则继续处理下一行 - 若遍历到该日期数据末尾仍未达到
@Counter = DailyAdjstVal,则从该日期第一行重新开始循环,直至满足条件 - 每个日期处理完成后,将该日期所有行的
SumOfUnitAdjustment更新为当日UnitAdjustment的总和(需等于DailyAdjstVal)
游标结构设计实现(SQL Server示例)
采用双层游标结构:外层游标遍历所有唯一日期,内层游标处理单个日期内的调整逻辑。
-- 声明变量 DECLARE @CurrentDate DATE DECLARE @Counter INT DECLARE @DailyAdjstVal INT DECLARE @RowID VARCHAR(10) DECLARE @CurrentEndingValue INT -- 外层游标:遍历所有唯一日期 DECLARE DateCursor CURSOR FOR SELECT DISTINCT 日期 FROM YourTableName ORDER BY 日期 ASC OPEN DateCursor FETCH NEXT FROM DateCursor INTO @CurrentDate WHILE @@FETCH_STATUS = 0 BEGIN -- 获取当日的总调整值 SELECT @DailyAdjstVal = MAX(DailyAdjstVal) FROM YourTableName WHERE 日期 = @CurrentDate SET @Counter = 0 -- 内层游标:循环处理当日数据,直到Counter达到DailyAdjstVal WHILE @Counter < @DailyAdjstVal BEGIN DECLARE InnerCursor CURSOR FOR SELECT ID, EndingValue FROM YourTableName WHERE 日期 = @CurrentDate ORDER BY StartingValue DESC OPEN InnerCursor FETCH NEXT FROM InnerCursor INTO @RowID, @CurrentEndingValue WHILE @@FETCH_STATUS = 0 AND @Counter < @DailyAdjstVal BEGIN IF @CurrentEndingValue > 3 BEGIN -- 更新当前行的调整值和结束值 UPDATE YourTableName SET UnitAdjustment = UnitAdjustment + 1, EndingValue = EndingValue - 1 WHERE 日期 = @CurrentDate AND ID = @RowID SET @Counter = @Counter + 1 -- 更新当前变量值,以便下一次判断 SET @CurrentEndingValue = @CurrentEndingValue - 1 END FETCH NEXT FROM InnerCursor INTO @RowID, @CurrentEndingValue END CLOSE InnerCursor DEALLOCATE InnerCursor END -- 更新当日的SumOfUnitAdjustment UPDATE YourTableName SET SumOfUnitAdjustment = (SELECT SUM(UnitAdjustment) FROM YourTableName WHERE 日期 = @CurrentDate) WHERE 日期 = @CurrentDate FETCH NEXT FROM DateCursor INTO @CurrentDate END CLOSE DateCursor DEALLOCATE DateCursor
注意事项
- 替换
YourTableName为实际数据表名 - 若ID不是唯一标识,需结合日期和ID作为更新条件,避免更新错误行
- 内层游标每次循环都会重新读取最新的
EndingValue,确保循环调整的正确性
内容的提问来源于stack exchange,提问作者SomekindaRazzmatazz
相关产品推荐
相关产品推荐

