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

标签 sql sql-server database tsql

我的程序需要将一个金额传递到查询中以执行此类计算,但在我的例子中,它逐行循环并扣除正确的金额,我知道这不是一种有效的实现方式。所以我在这里寻求更好的方法。

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

预期结果:

idx          amount          balance
1              50               0
2              30               0
3              20               0
4              50              20
5              60              60

非常感谢您的帮助。谢谢

最佳答案

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

  1. 第一个解决方案(SQL2005+;online query)

    声明@tbl1 表 ( idx INT IDENTITY(2,2) 主键, 金额 INT NOT NULL, 余额 INT 不为空 );

    INSERT INTO @tbl1 (amount,balance) VALUES (50, 50); INSERT INTO @tbl1 (amount,balance) VALUES (30, 30); INSERT INTO @tbl1 (amount,balance) VALUES (20, 20); INSERT INTO @tbl1 (amount,balance) VALUES (50, 50); INSERT INTO @tbl1 (amount,balance) VALUES (60, 60);

    声明@total_value_to_deduct INT; SET @total_value_to_deduct = 130;

    与 CteRowNumber 作为 ( 选择 *, ROW_NUMBER() OVER(ORDER BY idx) AS RowNum 来自@tbl1 ), Cte递归 作为 ( 选择 a.idx, a.数量, a.amount AS running_total, 案件 当 a.amount <= @total_value_to_deduct THEN 0 否则 a.amount - @total_value_to_deduct 结束为 new_balance, a.行号 FROM CteRowNumber a 其中 a.RowNum = 1 --AND a.amount < @total_value_to_deduct 联合所有 选择 crt.idx, crt.amount, crt.amount + prev.running_total AS running_total, 案件 当 crt.amount + prev.running_total <= @total_value_to_deduct THEN 0 当 prev.running_total < @total_value_to_deduct AND crt.amount + prev.running_total > @total_value_to_deduct THEN crt.amount + prev.running_total - @total_value_to_deduct ELSE crt.amount 结束为 new_balance, crt.RowNum 来自 CteRowNumber crt INNER JOIN CteRecursive prev ON crt.RowNum = prev.RowNum + 1 --WHERE prev.running_total < @total_value_to_deduct ) 更新@tbl1 设置余额 = b.new_balance 来自@tbl1a

  2. 方案二(SQL2012)

    更新@tbl1 设置余额 = b.new_balance 来自@tbl1 内部联接 ( 选择 x.idx, SUM(x.amount) OVER(ORDER BY x.idx) AS running_total, 案件 当 SUM(x.amount) OVER(ORDER BY x.idx) <= @total_value_to_deduct THEN 0 当 SUM(x.amount) OVER(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 结束为 new_balance 来自 @tbl1 x ) b ON a.idx = b.idx;

  3. 第三个解决方案 (SQ2000+) 使用 triangular join :

    更新@tbl1 设置余额 = d.new_balance 来自 @tbl1 e 内部联接 ( 选择 c.idx, 案件 当 c.running_total <= @total_value_to_deduct THEN 0 当 c.running_total - c.amount < @total_value_to_deduct --prev_running_total < @total_value_to_deduct AND c.running_total > @total_value_to_deduct 然后 c.running_total - @total_value_to_deduct ELSE c.amount 结束为 new_balance 从 ( 选择 a.idx, a.数量, (SELECT SUM(b.amount) FROM @tbl1 b WHERE b.idx <= a.idx) AS running_total 来自@tbl1 ) C )d ON d.idx = e.idx;

关于sql - 在 T-SQL 中执行此类计算逻辑的最佳方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9938828/

相关文章:

c# - LINQ to SQL Left Join 左边部分为空

sql - 如何查询名称中带有括号 ( ) 的 SQL 列?

php - 如何使单个值更新 SQL 查询与 Wordpress 中的 $wpdb 类一起使用?

mysql - SQL - 如何连接这个表?

sql - 更新表变量

Android SQLite 数据库,为什么要删除表并在升级时重新创建

SQL 数据透视函数

mysql - mySQL 的包装器使其看起来像 SQL Server?

Mysql:索引计数查询与维护汇总表

ruby-on-rails - 用于将数据加载到 Rails 应用程序的 ETL 框架