基于现有值将整数分配到列行(寻求集合式SQL方案)
集合式整数分配解决方案
要实现将指定整数分配到各行,使最终值尽可能接近(模拟每次给当前最小值加1的逻辑),可以通过排序+累计增量计算的集合式SQL完成,无需循环或游标,效率大幅提升。
核心思路
- 按原始值升序排序,优先给数值小的行分配更多增量,还原循环中每次选最小值加1的逻辑。
- 计算「填平」到某一行数值所需的累计增量:即把前面所有行的数值拉到当前行水平需要的总增量。
- 找到当前分配总量能覆盖的最大「填平」级别,计算剩余增量,再将剩余增量平均分配给前面的行(余数部分给前N行各加1)。
完整SQL实现
假设你的临时表为#Users,结构为UserIdMember INT, PreExistingAmount INT(对应问题中的Id和Bin),要分配的整数为@IntegerToDistribute:
DECLARE @IntegerToDistribute INT = 7; -- 可替换为20或其他值 WITH RankedUsers AS ( SELECT UserIdMember, PreExistingAmount, ROW_NUMBER() OVER (ORDER BY PreExistingAmount ASC) AS rn, COUNT(*) OVER () AS total_rows FROM #Users ), -- 递归计算累计填平到当前行数值所需的总增量 RecursiveCumulative AS ( SELECT UserIdMember, PreExistingAmount, rn, total_rows, CAST(0 AS BIGINT) AS cumulative_increment, PreExistingAmount AS current_level FROM RankedUsers WHERE rn = 1 UNION ALL SELECT ru.UserIdMember, ru.PreExistingAmount, ru.rn, ru.total_rows, rc.cumulative_increment + (ru.PreExistingAmount - rc.current_level) * (ru.rn - 1), ru.PreExistingAmount FROM RankedUsers ru JOIN RecursiveCumulative rc ON ru.rn = rc.rn + 1 ), -- 找到当前分配量能覆盖的最大填平级别 Threshold AS ( SELECT MAX(rn) AS max_rn, cumulative_increment, current_level FROM RecursiveCumulative WHERE cumulative_increment <= @IntegerToDistribute ), -- 计算剩余待分配的增量 Remaining AS ( SELECT @IntegerToDistribute - cumulative_increment AS remaining, max_rn, current_level FROM Threshold ) -- 计算最终结果 SELECT ru.UserIdMember, ru.PreExistingAmount + CASE -- 前max_rn行:先填平到current_level,再分配剩余增量 WHEN ru.rn <= Remaining.max_rn THEN (Remaining.current_level - ru.PreExistingAmount) + FLOOR(Remaining.remaining / Remaining.max_rn) + CASE WHEN ru.rn <= (Remaining.remaining % Remaining.max_rn) THEN 1 ELSE 0 END -- 超过max_rn的行:无需填平,仅在剩余增量有多余时分配 ELSE 0 END AS FinalAmount FROM RankedUsers ru CROSS JOIN Remaining ORDER BY ru.UserIdMember;
例子验证
例子1:@IntegerToDistribute=7
原始数据:
(1, 3), (2, 12), (3, 6)
执行后结果:
(1, 8), (2, 12), (3, 8)
计算过程:
- 排序后为
(1,3),(3,6),(2,12) - 填平到6需要3个增量(3→6),剩余
7-3=4个增量 - 4个增量平均分给前2行,每行加2,最终
3+3+2=8,6+0+2=8,12+0=12
例子2:@IntegerToDistribute=22(修正原例子总和矛盾)
若分配22,原始总和21+22=43,执行后结果:
(1, 15), (2, 14), (3, 14)
计算过程:
- 填平到12需要15个增量(3→12需9,6→12需6),剩余
22-15=7个增量 - 7个增量分给3行:每行先加2(3×2=6),剩余1个给第1行,最终
3+9+2+1=15,6+6+2=14,12+0+2=14
优势
- 纯集合式操作,避免循环/游标带来的性能问题,即使
@IntegerToDistribute极大也能快速计算 - 完全符合代码规范,逻辑清晰可维护
内容的提问来源于stack exchange,提问作者vera.nygren
相关产品推荐
相关产品推荐

