用普通T-SQL语句代替游标操作

经常写SQL的同学们都知道, 在Sql Server的IDE中如果SQL嵌套的层次太多, 查看和修改代码将变得非常痛苦, 再加上游标用得不恰当很容易出现"锁"和"性能"问题.

所以通常大大们都劝大家尽量少用游标, 阿飞个人很喜欢将业务逻辑写在存储过程里, 于是也经常遇到需要游标逻辑的地方, 这里就跟大家分享一点使用普通T-SQL语句代替游标的处理方案.

 

业务需求:
订单表 t_orders (id int, total money, paid money, date datetime)
付款单 t_pays (id int, amount money, date datetime)
付款单和订单关系表 t_relation (pay_id int, order_id int, amount money)

财务付款时, 往 t_pays 插入一条记录, 并且需要按照订单表的时间顺序在 t_relation 表标记出此次付款用于支付了哪些订单, 一个付款单可能会对应多个订单, 一个订单也可能分几次支付, 所以 t_relation 表中存储的是多对多的关系.

 

存储过程的输入参数 @amount money, 表示此次支付金额

1. 首先插入 t_pays 记录, set @pay_id = SCOPE_IDENTITY(); 获取此次付款单编号

2. 使用一个变量表来存储需要支付的订单

--amount 表示此订单需要支付的金额, pay 表示此次将要支付的金额, sort 用来表示支付顺序
declare @orders table(id int, amount money, pay money, sort int);  

--使用CTE(Common Table Expressions)列出所有需要支付的订单
with REF(id, amount, sort)
as
(
    select id, total - paid, Row_Number()over(order by date) from t_orders where total > paid
) --然后筛选出此次可以支付的订单, 插入到变量表
--需要注意的是, 普通订单此次支付的金额是 total - paid, 但是最后一个订单, 付款单金额如果不足(@amount < balance)就应该是 @amount - (balance - amount)
insert into @orders(id, amount, sort, pay)
select K.id, K.amount, K.sort, case when @amount >= T.balance then K.amount else @amount - (T.balance - K.amount) end from REF as K
outer apply
(
    select  IsNull(sum(amount), 0) as balance from REF where sort <= K.sort
)T
where @amount > T.balance - K.amount
--这里, T表累加了到K表当前记录为止的应付总额,
--可支付的订单应满足的条件是: 付款单金额@amount > 我前面一个订单为止的总额( 即 balance - amount)

 

3. 开始向关系表插入记录

insert into t_relation(pay_id, order_id, amount)
select
@pay_id,
id,
pay
from @orders;

 

4. 更新t_orders 表的 已付金额(paid)

update T set T.paid = T.paid + K.pay
from @orders K
inner join t_orders T on T.id = K.id

 

大功告成, 看起来好像有很多语句, 仔细数一下, 其实完成"关系链接"的只有三条语句.




posted @ 2012-08-28 14:36  疯子阿飞  阅读(2176)  评论(1编辑  收藏  举报