按顺序使用“选择顶部”和“删除顶部”是否安全?

时间:2010-04-06 21:32:54

标签: tsql

我经常在升级脚本中编写如下所示的T-SQL循环:

While Exists (Select * From #MyTable)
    Begin
    Declare @ID int, @Word nvarchar(max)
    Select Top 1 @ID=ID, @Word=[Word] From #MyTable
    -- Do something --
    Delete #MyTable Where ID=@ID
    End

运行一个款待,但我注意到了新的Delete Top功能,当#MyTable只是一个字符串列表时,它会很有用。在这种情况下,这会工作:

While Exists (Select * From #MyTable)
    Begin
    Declare @Word nvarchar(max)
    Select Top 1 @Word=[Word] From #MyTable
    -- Do something --
    Delete Top(1) #MyTable
    End

是的,它适用于我的测试脚本,但这样安全吗? Will Select Top 1和Delete Top(1)总是引用相同的记录,或者Top更模糊一点。

谢谢,Rob。

3 个答案:

答案 0 :(得分:3)

不,你的第二个例子不安全。

如果您打算使用“SELECT TOP ...”(并且您关心选择行的顺序),您应该使用“ORDER BY”来明确指定顺序。你可能在没有“ORDER BY”的情况下获得你想要的订单,但是当不使用ORDER BY时,SQL Server不保证返回行的顺序。

在“DELETE TOP ..”的情况下,你不能添加“ORDER BY”,所以你应该使用一个WHERE子句来唯一地标识要删除的行,就像你在你的第一个例子。

答案 1 :(得分:2)

如果您经常编写循环,要么您做错了,要么您的数据库需要重新设计,或者您要插入需要分批运行1000条左右的大批量数据。我敢打赌,几乎你所做的一切都可以以基于集合的方式完成并极大地提高性能。我无法告诉你如何在不知道发生什么样的事情的情况下做某些事情,但很可能通过使用案例启动来制作基于集合的事物。

答案 2 :(得分:0)

在这种情况下,您可以使用OUTPUT语句,而无需使用OUTPUT这样的ORDER BY:

DELETE #Origin_table
OUTPUT deleted.* INTO #Loop_Work

完整示例:

--DROP TABLE #Origin_table, #Loop_Work

CREATE TABLE #Origin_table (I INT IDENTITY, N VARCHAR(100))
INSERT #Origin_table VALUES
      ('Test A'), ('Test B'), ('Test C')
    , ('Test D'), ('Test E'), ('Test F')
    , ('Test G'), ('Test H'), ('Test I')


CREATE TABLE #Loop_Work (I INT, N VARCHAR(100))

WHILE EXISTS (SELECT TOP 1 1 FROM #Origin_table) BEGIN
    TRUNCATE TABLE #Loop_Work           --> Clear faster our table for loop

    DELETE TOP (3) #Origin_table        --> Delete top records
    OUTPUT deleted.* INTO #Loop_Work    --> Use the same records as OUTPUT and insert into the #Loop_Work
                                        --> The OUTPUT statement works similar as a trigger --> It's safe!

    SELECT * FROM #Loop_Work            --> Do what you need
END

如果要订购,请在DELETE中使用CTE (Common Table Expression)

TRUNCATE TABLE #Loop_Work

;WITH To_Delete AS ( --Remember the semicolon
    SELECT TOP 3 *
    FROM #Origin_table
        ORDER BY I DESC
    )
DELETE To_Delete --And use the CTE
OUTPUT deleted.* INTO #Loop_Work