[英]SQL Server-Query to traverse rows of table and updating sequentially
我有以下格式的请假表:
╔════╦═══════════╦═════════════════════╦═════════╦══════════╗
║ ID ║ Available ║ MaximumAccumulation ║ Availed ║ Priority ║
╠════╬═══════════╬═════════════════════╬═════════╬══════════╣
║ 1 ║ 10 ║ 4 ║ 0 ║ 1 ║
║ 2 ║ 15 ║ 5 ║ 0 ║ 2 ║
║ 3 ║ 8 ║ 3 ║ 0 ║ 3 ║
╚════╩═══════════╩═════════════════════╩═════════╩══════════╝
现在,如果用户申请10片叶子,则必须从第一行开始扣除(直到最大累积限制),在扣除之后,如果仍然有用户要累积的叶子(在我们的情况下,他仍然剩下6片叶子)应该从下一行中扣除。 该过程将继续进行,直到遍历所有行。 现在,如果他仍然留下树叶,则必须在表中插入新行,并带有:
ID:0,可用:0,最大累积量:0,可用(无论剩余多少)优先级:0
在我们的例子中,输出将是:
╔════╦═══════════╦═════════════════════╦═════════╦══════════╗
║ ID ║ Available ║ MaximumAccumulation ║ Availed ║ Priority ║
╠════╬═══════════╬═════════════════════╬═════════╬══════════╣
║ 1 ║ 6 ║ 4 ║ 4 ║ 1 ║
║ 2 ║ 9 ║ 5 ║ 6 ║ 2 ║
║ 3 ║ 8 ║ 3 ║ 0 ║ 3 ║
╚════╩═══════════╩═════════════════════╩═════════╩══════════╝
有人可以帮我写这样的查询吗?
我真的想不出基于集合的方法来执行此操作,因此我改用了程序! (如果我还是你,我建议这样做是在代码端而不是数据库端!)
以下存储的proc接受您的输入,并按行计算通过while循环可以取出多少。 它会为每一行执行此操作,直到行用完(此时将插入新行以及剩余的行),或者没有更多的时间可以起飞。
ALTER PROCEDURE [dbo].[UpdateLeave] @Days int
AS BEGIN
--Cursor Variables.
DECLARE @ID INT, @Available INT, @MaxAccum INT, @Availed INT, @Priority INT
--Inner While-Loop Variables
DECLARE @TotalAccumSoFar INT = 0, @RowAccumSoFar INT = 1
--Variable to check if the cursor is on the last row
DECLARE @RowCount INT = (SELECT COUNT(*) FROM Leave), @CurrentRow INT = 1
--Cursor to loop through the rows
DECLARE CURS CURSOR FOR SELECT ID, Available, MaxAccum, Availed, [Priority] FROM Leave WHERE Available > 0
OPEN CURS
FETCH NEXT FROM CURS INTO @ID, @Available, @MaxAccum, @Availed, @Priority
WHILE @@FETCH_STATUS = 0
BEGIN
--1. Use a while loop to take off as many days as possible from the current row.
WHILE @RowAccumSoFar <= @MaxAccum
BEGIN
UPDATE Leave
SET Available -= 1, Availed += 1
WHERE ID = @ID
SET @RowAccumSoFar += 1
SET @TotalAccumSoFar += 1
IF(@TotalAccumSoFar = @Days)
BEGIN
BREAK
END
END
--2. Check if we've taken off all the leave days
IF(@TotalAccumSoFar = @Days)
BEGIN
BREAK
END
--3. If not and we're on the last row, insert a new row.
IF(@RowCount = @CurrentRow)
BEGIN
INSERT INTO Leave (Available, MaxAccum, Availed, [Priority]) VALUES (0,0,(@Days - @TotalAccumSoFar),0)
END
--4. Update variables
SET @CurrentRow += 1
SET @RowAccumSoFar = 0
FETCH NEXT FROM CURS INTO @ID, @Available, @MaxAccum, @Availed, @Priority
END
CLOSE CURS
DEALLOCATE CURS
END
我唯一的区别是,在我的Leaves
表上, ID
是IDENTITY(1,1)
列,因此我不必处理插入内容上的ID。
结果
+----+-----------+--------------+---------+----------+
| id | Available | MaximumAccum | Availed | priority |
+----+-----------+--------------+---------+----------+
| 1 | 6 | 4 | 4 | 1 |
| 2 | 9 | 5 | 6 | 2 |
| 3 | 8 | 3 | 0 | 3 |
+----+-----------+--------------+---------+----------+
有点冗长,但是可以用。在我的机器上测试
CREATE TABLE [dbo].[T](
[Id] [int] NULL,
[Available] [int] NULL,
[MaximumAccumulation] [int] NULL,
[Availed] [int] NULL,
[Priority] [int] NULL
) ;
INSERT [dbo].[T] ([Id], [Available], [MaximumAccumulation], [Availed], [Priority]) VALUES (1, 10, 4, 0, 1)
INSERT [dbo].[T] ([Id], [Available], [MaximumAccumulation], [Availed], [Priority]) VALUES (2, 15, 5, 0, 2)
INSERT [dbo].[T] ([Id], [Available], [MaximumAccumulation], [Availed], [Priority]) VALUES (3, 8, 3, 0, 3)
询问
WITH CTE AS
(SELECT *,
CASE
WHEN LAG(t.Available,1) OVER(
ORDER BY t.id) IS NULL THEN (10 - t.MaximumAccumulation)
WHEN LEAD(t.Available,1) OVER(
ORDER BY t.id) IS NULL THEN t.Available
ELSE t.Available - (10 - LAG(t.MaximumAccumulation,1) OVER(
ORDER BY t.id))
END AS NewAvailable,
t.Available - CASE
WHEN LAG(t.Available,1) OVER(
ORDER BY t.id) IS NULL THEN (10 - t.MaximumAccumulation)
WHEN LEAD(t.Available,1) OVER(
ORDER BY t.id) IS NULL THEN t.MaximumAccumulation
ELSE t.Available - (10 - LAG(t.MaximumAccumulation,1) OVER(
ORDER BY t.id))
END AS NewAvailed
FROM t),
CTE2 AS
(SELECT *,
SUM(NewAvailed) OVER (
ORDER BY id) AS rollingsum
FROM CTE)
SELECT t.id,
CASE
WHEN rollingsum<=10 THEN NewAvailable
ELSE t.Available
END as Available,
t.MaximumAccumulation,
CASE
WHEN rollingsum<=10 THEN NewAvailed
ELSE t.Availed
END as Availed,
t.priority
FROM CTE2
JOIN t ON CTE2.id=t.id
结果
id Available MaximumAccumulation Availed priority
1 6 4 4 1
2 9 5 6 2
3 8 3 0 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.