如何在 SQL server 2012 的更新中使用聚合函数
How to use aggregate function in update in SQL server 2012
我试过如下图:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
预期输出:
Table 由员工 ID 以及分配给员工的金额组成,我需要根据员工使用情况从提交的金额中减去金额。应从 ID = 1 中扣除金额“10”,应从 ID = 2 中扣除金额“1”。
金额: 该特定员工可用的积分,具体取决于日期。
所以我需要根据条件从 table 中减少学分,首先我需要从旧学分中减去。在我的情况下,我需要从 empID = 1 收集 11 卢比,所以首先我需要从 ID=1 收集 10 卢比,从 ID=1 收集 1 卢比下一个信用即 ID=2。由于这个原因,在我对 ID=1 的预期输出中,值为 0,最终输出应该是
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
需要帮助更新记录。检查我的更新语句中的错误。
我认为您需要以下内容:从 11
中减去金额,余数为正。如果这是真的,这里有一个递归的解决方案 cte
:
DECLARE @t TABLE ( id INT, amount INT )
INSERT INTO @t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM @t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM @t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM @t t
JOIN cte c ON c.id = t.id
SELECT * FROM @t
输出:
id amount
1 0
2 0
3 1
4 2
这里我用17
作为起始余数
如果你使用 sql server 2012+
那么你可以这样做:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM @t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
首先你应该得到金额的累计总和:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
从这里开始,我们应该在 running_sum 超过值 11 之前的行上放置 0。更新 运行 总和超过 11 的行,并且不对前行之后的行执行任何操作。
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
从这里我们可以进行更新:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
看到 SQLDFIDDLE
Declare @Deduct int = -11,
@CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set @CurrentDeduct = case when abs(@Deduct) >= AMOUNT then Amount else abs(@Deduct) end
, @Deduct = @Deduct + @CurrentDeduct
,AMOUNT = AMOUNT - @CurrentDeduct
where EmpID= 1
我试过如下图:
CREATE TABLE #TEMP
(
ID INT,
EmpID INT,
AMOUNT INT
)
INSERT INTO #TEMP VALUES(1,1,10)
INSERT INTO #TEMP VALUES(2,1,5)
INSERT INTO #TEMP VALUES(3,2,6)
INSERT INTO #TEMP VALUES(4,3,8)
INSERT INTO #TEMP VALUES(5,3,10)
.
.
.
SELECT * FROM #TEMP
ID EmpID AMOUNT
1 1 10
2 1 5
3 2 6
4 3 8
5 4 10
UPDATE #TEMP
SET AMOUNT = SUM(AMOUNT) - 11
Where EmpID = 1
预期输出: Table 由员工 ID 以及分配给员工的金额组成,我需要根据员工使用情况从提交的金额中减去金额。应从 ID = 1 中扣除金额“10”,应从 ID = 2 中扣除金额“1”。
金额: 该特定员工可用的积分,具体取决于日期。
所以我需要根据条件从 table 中减少学分,首先我需要从旧学分中减去。在我的情况下,我需要从 empID = 1 收集 11 卢比,所以首先我需要从 ID=1 收集 10 卢比,从 ID=1 收集 1 卢比下一个信用即 ID=2。由于这个原因,在我对 ID=1 的预期输出中,值为 0,最终输出应该是
ID EmpID AMOUNT
1 1 0
2 1 4
3 2 6
4 3 8
5 4 10
需要帮助更新记录。检查我的更新语句中的错误。
我认为您需要以下内容:从 11
中减去金额,余数为正。如果这是真的,这里有一个递归的解决方案 cte
:
DECLARE @t TABLE ( id INT, amount INT )
INSERT INTO @t VALUES
( 1, 10 ),
( 2, 5 ),
( 3, 3 ),
( 4, 2 );
WITH cte
AS ( SELECT * , 17 - amount AS remainder
FROM @t
WHERE id = 1
UNION ALL
SELECT t.* , c.remainder - t.amount AS remainder
FROM @t t
CROSS JOIN cte c
WHERE t.id = c.id + 1 AND c.remainder > 0
)
UPDATE t
SET amount = CASE WHEN c.remainder > 0 THEN 0
ELSE -remainder
END
FROM @t t
JOIN cte c ON c.id = t.id
SELECT * FROM @t
输出:
id amount
1 0
2 0
3 1
4 2
这里我用17
作为起始余数
如果你使用 sql server 2012+
那么你可以这样做:
WITH cte
AS ( SELECT * ,
17 - SUM(amount) OVER ( ORDER BY id ) AS remainder
FROM @t
)
SELECT id ,
CASE WHEN remainder >= 0 THEN 0
WHEN remainder < 0
AND LAG(remainder) OVER ( ORDER BY id ) >= 0
THEN -remainder
ELSE amount
END
FROM cte
首先你应该得到金额的累计总和:
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP;
从这里开始,我们应该在 running_sum 超过值 11 之前的行上放置 0。更新 运行 总和超过 11 的行,并且不对前行之后的行执行任何操作。
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
);
从这里我们可以进行更新:
merge into #TEMP t
using (
select
id,
amount
running_sum,
min(case when running_sum > 11 then id end) over () as decide
from (
select
id,
amount,
sum(amount) over (order by id) running_sum
from #TEMP
)
)a on a.id=t.id
when matched then update set
t.amount = case when a.id = a.decide then a.running_sum - 11
when a.id < a.decide then 0
else a.amount
end;
看到 SQLDFIDDLE
Declare @Deduct int = -11,
@CurrentDeduct int = 0 /*this represent the deduct per row */
update #TEMP
set @CurrentDeduct = case when abs(@Deduct) >= AMOUNT then Amount else abs(@Deduct) end
, @Deduct = @Deduct + @CurrentDeduct
,AMOUNT = AMOUNT - @CurrentDeduct
where EmpID= 1