在更新表时将值分配给所有行

时间:2014-08-13 12:32:42

标签: sql sql-server sql-server-2008

我有像tblCustData

这样的表结构
ID    UserID    Fee FeePaid
 1      12      150   0
 2      12      100   0
 3      12      50    0

要在FeePaid列中更新值,以便@Amt中的200变量中有值,那么它应该更新任何两行

输出应该像

ID    UserID    Fee FeePaid
1      12       150   150
2      12       100   50
3      12       50    0

FeePaid不应该比Fee列更重要但如果我在350变量中传递@Amt,它应该产生类似

的输出
ID    UserID    Fee FeePaid
1       12      150   200
2       12      100   100
3       12      50    50

仅当@Amt超过Fee列中的总值

我想不出这个问题

Update tblCustData
Set FeePaid=@Amt
Where UserID=12

3 个答案:

答案 0 :(得分:5)

首先使用CTE语法,我们准备一个带有sums分布的表,然后使用唯一字段Code使用CASE更新主表来处理所有可能的方式(包括带有余数的第一行)。

Declare @Amt int;
SET @Amt=250;


with T as 
(
   SELECT ROW_NUMBER() OVER (ORDER BY Fee desc) as rn, *
   FROM tblCustData WHERE UserId=12
)  
,T2 as 
(
   SELECT *,
          ISNULL((SELECT SUM(Fee-FeePaid) FROM T WHERE T1.RN<RN),0) as PrevSum 
   FROM T as T1
 )

UPDATE
    A
SET A.FeePaid = A.FeePaid+ CASE WHEN (B.PrevSum+B.Fee-B.FeePaid<=@Amt) 
                                     AND (B.RN<>1) 
                                        THEN B.Fee-B.FeePaid
                     WHEN (B.PrevSum+B.Fee-B.FeePaid<=@Amt) AND (B.RN=1) 
                                        THEN @Amt-B.PrevSum
                     WHEN B.PrevSum>=@Amt 
                                        THEN 0                 
                     WHEN B.PrevSum+B.Fee-B.FeePaid>@Amt 
                                        THEN @Amt-B.PrevSum
              END
FROM
    tblCustData A
    JOIN T2 B ON A.Code = B.Code
GO

SQLFiddle demo

答案 1 :(得分:1)

试试..

declare @t table (id int identity, UserId int, Fee money, FeePaid money)

insert into @t (UserID,  Fee, FeePaid)
values
(12,      150,   0)
,(12,      100,   0)
,(12,      50 ,   0)

declare @amt money = 200;   -- change to 400 to test over paid

declare @Fees money;
select @Fees = sum(Fee) from @t;

declare @derivedt table (deid int, id int, UserId int, Fee money, FeePaid money)

insert into @derivedt (deid, id, UserId, Fee, FeePaid)
select row_number() over (order by case when @amt <= @Fees then id else -id end asc), id, UserId, Fee, FeePaid
    from @t
    -- order by case when @amt <= @Fees then id else -id end asc

; with cte(deid, id, UserId, Fee, FeePaid, Remainder)
as
(
    select 0 as deid, 0 as id, 0 as UserId, cast(0.00 as money) as Fee, cast(0.00 as money) as FeePaid , @Amt as Remainder
    from @derivedt
    where id = 1
    union all
    select t.deid, t.id, t.UserId, t.Fee, case when cte.Remainder > t.Fee then t.Fee else cte.Remainder end as FeePaid
        , case when cte.Remainder > t.Fee then cte.Remainder - t.Fee else 0 end as Remainder
    from @derivedt t inner join cte cte on t.deid = (cte.deid + 1)
)

update origt
set FeePaid = det.FeePaid
from @t origt
    inner join
    (
    select cte1.deid, cte1.id, cte1.UserId, cte1.Fee, cte1.FeePaid + ISNULL(cte2.Remainder, 0) as FeePaid
    from cte cte1
        left outer join (select top 1 deid, Remainder from cte order by deid desc) cte2
        on cte1.deid = cte2.deid
    where cte1.deid > 0
    ) det
    on origt.id = det.id

select *
from @t

修改为持续更新值..

    -- Create table once and insert into table once
create table #t (id int identity, UserId int, Fee money, FeePaid money)

insert into #t (UserID,  Fee, FeePaid)
values
(12,      150,   0)
,(12,      100,   0)
,(12,      50 ,   0)

-- ===============================

-- Run multiple times to populate #t table 
declare @amt money = 100;   -- change to 400 to test over paid

declare @Fees money;
select @Fees = sum(Fee - FeePaid) from #t;

declare @derivedt table (deid int, id int, UserId int, Fee money, FeePaid money)

insert into @derivedt (deid, id, UserId, Fee, FeePaid)
select row_number() over (order by case when @amt <= @Fees then id else -id end asc), id, UserId, (Fee - FeePaid) as Fee, FeePaid
    from #t
    -- order by case when @amt <= @Fees then id else -id end asc

; with cte(deid, id, UserId, Fee, FeePaid, Remainder)
as
(
    select 0 as deid, 0 as id, 0 as UserId, cast(0.00 as money) as Fee, cast(0.00 as money) as FeePaid , @Amt as Remainder
    from @derivedt
    where id = 1
    union all
    select t.deid, t.id, t.UserId, t.Fee, case when cte.Remainder >= t.Fee then t.Fee else cte.Remainder end as FeePaid
        , case when cte.Remainder >= t.Fee then cte.Remainder - t.Fee else 0 end as Remainder
    from @derivedt t inner join cte cte on t.deid = (cte.deid + 1)
)


update origt
set FeePaid = origt.FeePaid + det.FeePaid
from #t origt
    inner join
    (
    select cte1.deid, cte1.id, cte1.UserId, cte1.Fee, cte1.FeePaid + ISNULL(cte2.Remainder, 0) as FeePaid, cte1.Remainder
    from cte cte1
        left outer join (select top 1 deid, Remainder from cte order by deid desc) cte2
        on cte1.deid = cte2.deid
    where cte1.deid > 0
    ) det
    on origt.id = det.id

select *
from #t


-- Drop temp table after
-- drop table #t

答案 2 :(得分:-2)

除了您的代码,我在您的表中添加了一个标识列。见代码。

DECLARE @TAB TABLE(ID INT IDENTITY(1,1),USERID INT, FEE INT, FEEPAID INT)
INSERT INTO @TAB VALUES (12,150,0),(12,100,0),(12,50,0)

DECLARE @AMOUNT INT = 230,
        @AMOUNTNEW INT = 0,
        @B INT = 1,
        @S INT = 1,@E INT = (SELECT COUNT(*) FROM @TAB)

WHILE   @S <= @E
BEGIN
    UPDATE  LU
    SET     LU.FEEPAID = CASE WHEN @AMOUNT >= FEE THEN FEE ELSE @AMOUNT END
    FROM    @TAB LU
    WHERE   LU.ID = @S

    SET     @AMOUNT = @AMOUNT - (SELECT FEE FROM @TAB WHERE ID = @S)
    IF      @AMOUNT <= 0  
            SET @S = @E

    SET     @S = @S + 1

END

SELECT * FROM @TAB

<强>结果:

enter image description here

我希望这个想法很明确,我们可以从这里开始工作。