7

我在数据库中有一个表。

账单

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
4

3 回答 3

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);
于 2013-08-19T16:21:29.953 回答
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 小提琴演示

对于 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;

sql 小提琴演示

于 2013-08-19T16:29:01.807 回答
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
于 2013-08-19T16:30:44.497 回答