繁体   English   中英

SQL Server查询遍历表行并顺序更新

[英]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表上, IDIDENTITY(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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM