【问题标题】:How to set variables in While Loop in Sql Server如何在 SQL Server 的 While 循环中设置变量
【发布时间】: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

【问题讨论】:

    标签: sql-server stored-procedures while-loop cursor


    【解决方案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)
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-26
      • 2014-03-02
      • 2020-03-01
      • 2019-03-01
      • 2020-03-27
      • 2018-10-28
      • 2018-02-24
      • 1970-01-01
      相关资源
      最近更新 更多