如何在 SQL Server 的 While 循环中设置变量

Posted

技术标签:

【中文标题】如何在 SQL Server 的 While 循环中设置变量【英文标题】:How to set variables in While Loop in Sql Server 【发布时间】:2016-09-13 11:26:35 【问题描述】:

我正在尝试在 SQL SERVER 中使用 while 循环而不是 CURSOR。我正在尝试在 while 中选择 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

【问题讨论】:

【参考方案1】:

另一种选择:

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

但是,从一个表中一个一个地删除所有记录可能是一个性能地狱。您可能要考虑改用TRUNCATE TABLE

TRUNCATE TABLE #wmsorder

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

使用包含 100,000 行的临时表进行测试,一一删除行花了我 9 秒,而 truncate 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

【讨论】:

我正在一一删除行,因为我正试图摆脱我使用的行。 我明白了。在这种情况下,您可能需要重新考虑整个脚本。您可能会找到一种基于集合的方式来处理您对这些数据所做的任何事情,而不是逐个使用行。【参考方案2】:
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

【讨论】:

这是最快的吗?我从来没想过这种方法 @ayilmaz,迭代处理总是很慢【参考方案3】:

请参阅下面的代码。我刚刚更正了你分享的SQL语句

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

但正如 Zohar Peled 所提到的,如果您从表中逐个删除记录,这将对引擎造成痛苦。所以下面我分享了另一个查询,通过这个,你甚至可以在从表中删除之前跟踪记录

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)

抱歉,我没有测试第二个代码。如果它破坏了某些东西,请原谅我。但我很确定它可能需要进行小修复才能使此查询正常运行。一切顺利。

【讨论】:

以上是关于如何在 SQL Server 的 While 循环中设置变量的主要内容,如果未能解决你的问题,请参考以下文章

如何删除 MS SQL Server 存储过程中的 While 循环? [关闭]

Sql server 游标与 While 循环的性能

sql server中do while循环怎么写

在 SQL Server 2008 中执行 while 循环

避免 SQL Server 中的 while 循环

在 SQL Server 中使用插入语句优化必要的 while 循环