在T-SQL中执行此类计算逻辑的最佳方法

sky*_*lls 6 sql t-sql database sql-server

我的程序需要将一定数量传入查询以执行此类计算,但在我的情况下,它逐行循环并扣除正确的数量,我知道这不是一种有效的实现方式.所以我在这里寻求更好的方式.

PS:这只是我的草稿代码,我很抱歉由于某些原因我无法发布完整的源代码.现在我重新构建了我的代码,使其更加完整和合理.

   --- the amount column is just for reference.

    insert into tbl1 (idx,amount,balance) values (1, 50, 50)
    insert into tbl1 (idx,amount,balance) values (2, 30, 30)
    insert into tbl1 (idx,amount,balance) values (3, 20, 20)
    insert into tbl1 (idx,amount,balance) values (4, 50, 50)
    insert into tbl1 (idx,amount,balance) values (5, 60, 60)


declare @total_value_to_deduct int
declare @cs_index int, @cs_balance int, @deduct_amount int

set @total_value_to_deduct = 130

declare csDeduct Cursor for select idx, balance from tbl1 where balance > 0 
open csDeduct fetch next from csDeduct into @cs_index, @cs_balance

while @@FETCH_STATUS = 0 and @total_value_to_deduct > 0
begin

   if @cs_balance >= @total_value_to_deduct  
    set @deduct_amount = @total_value_to_deduct
   else
    set @deduct_amount = @cs_balance

    -- contine deduct row by row if the total_value_to_deduct is not 0
    set @total_value_to_deduct = @total_value_to_deduct - @deduct_amount

    update tbl1 set balance = balance - @deduct_amount  where idx = @cs_index
    fetch next from csDeduct into @cs_index, @cs_balance
end

close csDeduct
deallocate csDeduct
Run Code Online (Sandbox Code Playgroud)

预期结果 :

idx          amount          balance
1              50               0
2              30               0
3              20               0
4              50              20
5              60              60
Run Code Online (Sandbox Code Playgroud)

你必须得到你的帮助.谢谢

Bog*_*ean 4

修订1:我添加了第三个解决方案

  1. 第一种解决方案(SQL2005+;在线查询

    DECLARE @tbl1 TABLE ( idx INT IDENTITY(2,2) PRIMARY KEY, amount INT NOT NULL, Balance INT NOT NULL );

    INSERT INTO @tbl1 (金额,余额) VALUES (50, 50); INSERT INTO @tbl1 (金额,余额) VALUES (30, 30); INSERT INTO @tbl1 (金额,余额) VALUES (20, 20); INSERT INTO @tbl1 (金额,余额) VALUES (50, 50); INSERT INTO @tbl1 (金额,余额) VALUES (60, 60);

    声明@total_value_to_deduct INT;设置@total_value_to_deduct = 130;

    AND CteRowNumber AS ( SELECT *, ROW_NUMBER() OVER(ORDER BY idx) AS RowNum FROM @tbl1 a ), CteRecursive AS ( SELECT a.idx, a.amount, a.amount AS running_total, CASE WHEN a.amount <= @ Total_value_to_deduct THEN 0 ELSE a.amount - @total_value_to_deduct END AS new_balance, a.RowNum FROM CteRowNumber a WHERE a.RowNum = 1 --AND a.amount < @total_value_to_deduct UNION ALL SELECT crt.idx, crt.amount, crt.amount + prev.running_total为running_total,crt.amount + prev.running_total <= @total_value_to_deduct,然后0 wher prev.running_total <@total_value_value_to_dededuct and crt.amount and crt.running + prev.running_total> @total> @total> @total> @total> @total> @total> @talue_deftal-crt croll-crt crolliunt -crt crold crold crolliunt- alue_to_deduct ELSE crt.amount END AS new_balance, crt.RowNum FROM CteRowNumber crt INNER JOIN CteRecursive prev ON crt.RowNum = prev.RowNum + 1 --WHERE prev.running_total < @total_value_to_deduct ) UPDATE @tbl1 SET 余额 = b.new_balance FROM @tbl1 A

  2. 第二种解决方案(SQL2012)

    UPDATE @tbl1 SET 余额 = b.new_balance FROM @tbl1 a INNER JOIN ( SELECT x.idx, SUM(x.amount) OVER(ORDER BY x.idx) AS running_total, CASE WHEN SUM(x.amount) OVER(ORDER BY x.idx) <= @total_value_to_deduct THEN 0 当 SUM(x.amount) 超过(ORDER BY x.idx) - x.amount < @total_value_to_deduct --prev_running_total < @total_value_to_deduct AND SUM(x.amount) OVER(ORDER BY x .idx) > @total_value_to_deduct THEN SUM(x.amount) OVER(ORDER BY x.idx) - @total_value_to_deduct ELSE x.amount END AS new_balance FROM @tbl1 x ) b ON a.idx = b.idx;

  3. 第三种解决方案(SQ2000+)使用三角连接

    UPDATE @tbl1 SET Balance = d.new_balance FROM @tbl1 e INNER JOIN ( SELECT c.idx, CASE WHEN c.running_total <= @total_value_to_deduct THEN 0 WHEN c.running_total - c.amount < @total_value_to_deduct --prev_running_total < @total_value_to_deduct AND c.running_total > @total_value_to_deduct THEN c.running_total - @total_value_to_deduct ELSE c.amount END AS new_balance FROM ( SELECT a.idx, a.amount, (SELECT SUM(b.amount) FROM @tbl1 b WHERE b.idx <= a .idx) AS running_total FROM @tbl1 a ) c )d ON d.idx = e.idx;