如何在 Sql 服务器的 While 循环中设置变量

How to set variables in While Loop in Sql Server

我正在尝试在 SQL 服务器中使用 while 循环而不是 CURSOR。我正在尝试 select TOP 1 并将它们设置为如下所示的变量。它不允许我在 while 循环中设置变量。我做错了什么?

WHILE (
        SELECT TOP 1 @WAOR_CODE = WAOR_.WAOR_CODE
                   , @WAOD_INVENTORYITEMID = WAOD_.WAOD_INVENTORYITEMID
        FROM #wmsorder
    )
BEGIN
    SELECT @WAOR_CODE
         , @WAOD_INVENTORYITEMID

    DELETE TOP (1) #wmsorder
END
DECLARE @t TABLE (a INT PRIMARY KEY)
INSERT INTO @t
VALUES (1), (2), (3)

变体 #1:

label:
    DELETE TOP(1)
    FROM @t
    OUTPUT DELETED.a
IF @@ROWCOUNT > 0
    GOTO label

变体 #2:

WHILE @@ROWCOUNT != 0
    DELETE TOP(1)
    FROM @t
    OUTPUT DELETED.a

变体 #3:

DECLARE @a TABLE(a INT)

WHILE @@ROWCOUNT != 0 BEGIN

    DELETE FROM @a

    DELETE TOP(1)
    FROM @t
    OUTPUT DELETED.a INTO @a

    SELECT * FROM @a

END

另一个选项:

WHILE EXISTS(select 1 FROM #wmsorder)
BEGIN
    DELETE TOP (1)
    FROM #wmsorder
END

但是,从 table 中一条一条地删除所有记录可能会导致性能下降。您可能需要考虑改用 TRUNCATE TABLE

TRUNCATE TABLE #wmsorder

另外,请注意每次删除都会写入数据库日志,而截断 table 根本不会写入日志。

使用包含 100,000 行的临时 table 进行测试,一行一行地删除行花了我 9 秒,而截断 table 立即完成:

-- create and populate sample table
SELECT TOP 100000 IDENTITY(int,1,1) AS Number
    INTO #wmsorder
    FROM sys.objects s1
    CROSS JOIN sys.objects s2

    -- delete rows one by one
    WHILE EXISTS(select 1 FROM #wmsorder)
    BEGIN
        DELETE TOP (1)
        FROM #wmsorder
    END

-- clean up
DROP TABLE #wmsorder



-- create and populate sample table
SELECT TOP 100000 IDENTITY(int,1,1) AS Number
    INTO #wmsorder
    FROM sys.objects s1
    CROSS JOIN sys.objects s2

-- truncate the table
TRUNCATE TABLE #wmsorder

-- clean up
DROP TABLE #wmsorder

See the below code. I just corrected the SQL statements shared by you

WHILE 1=1
BEGIN
        IF NOT EXISTS (SELECT 1 FROM #wmsorder)
            BREAK
        SELECT TOP 1 @WAOR_CODE = WAOR_.WAOR_CODE
            ,@WAOD_INVENTORYITEMID = WAOD_.WAOD_INVENTORYITEMID
        FROM #wmsorder WAOR_

        SELECT @WAOR_CODE
        ,@WAOD_INVENTORYITEMID

        DELETE #wmsorder WHERE WAOR_CODE = @WAOR_CODE AND WAOD_INVENTORYITEMID = @WAOD_INVENTORYITEMID
END

But as Zohar Peled mentioned, it will be a pain to the engine if you are deleting the records one by one from a table. So below I have shared another query, through this even you can track the records before deleting from the table

DECLARE @TableVar AS TABLE (WAOR_CODE VARCHAR(100), WAOD_INVENTORYITEMID VARCHAR(100))
    WHILE 1=1
    BEGIN
            SELECT TOP 1 @WAOR_CODE = WAOR_.WAOR_CODE
                ,@WAOD_INVENTORYITEMID = WAOD_.WAOD_INVENTORYITEMID
            FROM #wmsorder WAOR_
            WHERE NOT EXISTS (SELECT 1 FROM @TableVar t WHERE t.WAOR_CODE = WAOR_.WAOR_CODE AND t.WAOD_INVENTORYITEMID = WAOR_.WAOD_INVENTORYITEMID)
            IF @WAOR_CODE IS NULL AND @WAOD_INVENTORYITEMID IS NULL
                BREAK

            INSERT INTO @TableVar
            (WAOR_CODE, WAOD_INVENTORYITEMID)
            SELECT @WAOR_CODE
            ,@WAOD_INVENTORYITEMID
    END
DELETE #wmsorder WHERE EXISTS (SELECT 1 FROM @TableVar t WHERE t.WAOR_CODE = #wmsorder.WAOR_CODE AND t.WAOD_INVENTORYITEMID = #wmsorder.WAOD_INVENTORYITEMID)

Sorry I did not test the second code. Please forgive me if it breaks something. But I am pretty sure it may require a small repair to make this query functional. All the best.