我在数据库中有一个表。
ID Total Paid Status
1 1000 1000 Paid
2 500 400 Part Paid
3 700 0 Unpaid
4 200 0 Unpaid
现在用户支付PAID_AMT - > 900美元,我希望分发这样我的桌子看起来:
ID Total Paid Status
1 1000 1000 Paid
2 500 500 Paid
3 700 700 Paid
4 200 100 Part Paid
可以使用游标轻松完成,但我想避免使用游标。
是否可以使用带有输出参数的简单更新查询来实现此目的。
像这样的东西
Update Bill
Set Paid = Total,
Status = 'Paid',
Output PAID_AMT = PAID_AMT - (Total-Paid )
where Total-Paid > PAID_AMT
答案 0 :(得分:4)
以下查询显示了欠款额,假设SQL Server 2012:
select b.*,
sum(total - paid) over (order by id) as cumNotPaid
from bill b
您现在可以分配金额:
select b.*,
(case when cumNotPaid >= @AMOUNT then 0
when cumNotPaid - toBePaid <= @AMOUNT then toBePaid
else @AMOUNT - cumnotPaid
end) as PaidAmount
from (select b.*,
sum(total - paid) over (order by id) as cumNotPaid,
total - paid as ToBePaid
from bill b
) b
现在,这是一个可更新的CTE,因此我们可以在更新语句中使用它:
with toupdate as (
(select b.*,
(case when cumNotPaid >= @AMOUNT then 0
when cumNotPaid - toBePaid <= @AMOUNT then toBePaid
else @AMOUNT - cumnotPaid
end) as PaidAmount
from (select b.*,
sum(total - paid) over (order by id) as cumNotPaid,
total - paid as ToBePaid
from bill b
) b
)
update toupdate
set paid = PaidAmount,
status = (case when total = paid then 'Paid' when total = 0 then 'UnPaid'
else 'PartPaid'
end);
答案 1 :(得分:3)
我不知道你是什么版本的SQL服务器,如果是2008那么你就不能使用滚动和与窗口函数。您可以尝试这种递归查询:
declare @paid_amount int = 900
;with cte1 as (
select
b.id,
b.total - b.paid as diff,
row_number() over(order by id) as row_num
from Bill as b
where b.total <> b.paid
), cte2 as (
select
c1.id, c1.row_num, @paid_amount - c1.diff as paid_amount
from cte1 as c1
where c1.row_num = 1
union all
select
c1.id, c1.row_num, c2.paid_amount - c1.diff as paid_amount
from cte1 as c1
inner join cte2 as c2 on c2.row_num + 1 = c1.row_num
where c2.paid_amount > 0
)
update Bill set
Paid =
case
when c.paid_amount >= 0 then b.Total
else b.Total - b.Paid + c.paid_amount
end,
Status = case when c.paid_amount >= 0 then 'Paid' else 'Part Paid' end
from Bill as b
inner join cte2 as c on c.id = b.id
对于SQL Server 2012,它更容易一些:
declare @paid_amount int = 900
;with cte1 as (
select
b.id,
b.total - b.paid as amount_to_pay,
sum(b.total - b.paid) over(order by b.id) as amount
from Bill as b
where b.total <> b.paid
), cte2 as (
select
c.id,
@paid_amount - c.amount as remain_amount
from cte1 as c
where @paid_amount - c.amount + c.amount_to_pay >= 0
)
update Bill set
Paid =
case
when c.remain_amount >= 0 then b.Total
else b.Total - b.Paid + c.remain_amount
end,
Status = case when c.remain_amount >= 0 then 'Paid' else 'Part Paid' end
from Bill as b
inner join cte2 as c on c.id = b.id;
答案 2 :(得分:1)
如果您使用的是SQL 2012,则可以使用以下命令:
DECLARE @PayAmount INT = 900;
WITH Dues AS
(
SELECT *, Total-Paid AS Due
FROM Bill
)
, Cumulative AS
(
SELECT *, SUM(Due) OVER (ORDER BY Id) AS CumulativeTotalDue
FROM Dues
)
, Payable AS
(
SELECT *, @PayAmount - CumulativeTotalDue AS AmountLeftAfterPaying
FROM Cumulative
)
, BillWithAmountToApplyRaw AS
(
SELECT *
, CASE
WHEN AmountLeftAfterPaying >= 0 THEN Due
ELSE Due + AmountLeftAfterPaying
END AS RawAmountToApply
FROM Payable
)
, BillWithAmountToApply AS
(
SELECT *, CASE WHEN RawAmountToApply < 0 THEN 0 ELSE RawAmountToApply END AS AmountToApply
FROM BillWithAmountToApplyRaw
)
这将为您提供在AmountToApply列中应用的金额。 所以你可以使用上面的
UPDATE BillWithAmountToApply
SET Paid = Paid + AmountToApply
FROM BillWithAmountToApply
(使用
SELECT *
FROM BillWithAmountToApply
如果你想要先检查出来
SQL 2008版本(由于重复连接效率较低,2012年不需要):
WITH Dues AS
(
SELECT *, Total-Paid AS Due
FROM Bill
)
, CumulativeDue AS
(
SELECT base.Id, SUM(cumulative.Due) AS CumulativeTotalDue
FROM Dues base
JOIN Dues cumulative ON cumulative.Id <= base.Id
GROUP BY base.Id
)
, Cumulative AS
(
SELECT Dues.*, CumulativeDue.CumulativeTotalDue
FROM Dues
JOIN CumulativeDue ON CumulativeDue.Id = Dues.Id
)
... as above
架构对象:
--BEGIN TRAN;
--CREATE TABLE Bill
--(
-- ID Int PRIMARY KEY IDENTITY,
-- Total INT NOT NULL,
-- Paid INT NOT NULL DEFAULT(0),
-- Status AS
-- CASE
-- WHEN Paid = Total THEN 'Paid'
-- WHEN Paid = 0 THEN 'Unpaid'
-- ELSE 'Part Paid'
-- END
--);
--WITH KnownValues(Total, Paid) AS
--(
-- SELECT 1000, 1000
-- UNION ALL SELECT 500, 400
-- UNION ALL SELECT 700, 0
-- UNION ALL SELECT 200, 0
--)
--INSERT INTO Bill(Total, Paid)
--SELECT *
--FROM KnownValues;
--COMMIT