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

Tsql 按顺序使用“选择顶部”和“删除顶部”是否安全?,tsql,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

我经常在升级脚本中编写这样的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
是的,它在我的测试脚本中起作用,但这安全吗?将选择Top 1并删除Top(1)始终引用同一条记录,或者Top有点模糊


谢谢你,Rob。

如果你经常写循环,要么是你做错了什么,要么是你的数据库需要重新设计,要么是你插入了大量的数据,这些数据需要以1000条左右的记录批量运行。我敢打赌,您正在做的几乎所有事情都可以以基于集合的方式完成,并极大地提高性能。如果不知道在做某件事时会发生什么情况,我无法向您演示如何进行设置,但很可能可以使用case startement进行设置。

如果您经常编写循环,则可能是您做错了什么,或者您的数据库需要重新设计,或者您插入了大量数据,这些数据需要以大约1000条记录的批次运行。我敢打赌,您正在做的几乎所有事情都可以以基于集合的方式完成,并极大地提高性能。如果不知道在做某件事时会发生什么情况,我无法向您演示如何进行设置,但很可能可以使用case startement进行设置。

不,您的第二个示例不安全

如果您要使用“选择顶部…”(并且您关心行的选择顺序),那么您应该真正使用“order BY”来明确指定顺序。如果不使用“order BY”,您可能会获得所需的订单,但如果不使用order BY,SQL Server不会保证返回行的顺序


在“DELETE TOP..”的情况下,您不能添加“ORDER BY”,因此您应该使用WHERE子句唯一地标识要删除的行,就像您在第一个示例中所做的那样

不,您的第二个示例不安全

如果您要使用“选择顶部…”(并且您关心行的选择顺序),那么您应该真正使用“order BY”来明确指定顺序。如果不使用“order BY”,您可能会获得所需的订单,但如果不使用order BY,SQL Server不会保证返回行的顺序


在“DELETE TOP..”的情况下,您不能添加“ORDER BY”,因此您应该使用WHERE子句唯一地标识要删除的行,就像您在第一个示例中所做的那样

在这种情况下,您可以使用OUTPUT语句,您不需要像这样使用OUTPUT命令:

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
如果要订购,请在删除中使用:

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

在这种情况下,您可以使用OUTPUT语句,您不需要像这样使用OUTPUT命令:

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
如果要订购,请在删除中使用:

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

+1,循环+数据库=慢。但是,OP没有使用光标!我从没说过他是。但他所做的很有可能是以一种基于集合的方式完成的。对不起,我应该澄清一下“我经常在升级脚本中编写循环”。。。在实际的应用程序中永远不会做这样的事情+1,循环+数据库=慢。但是,OP没有使用光标!我从没说过他是。但他所做的很有可能是以一种基于集合的方式完成的。对不起,我应该澄清一下“我经常在升级脚本中编写循环”。。。在实际的应用程序中永远不会做这样的事情!谢谢,我有点怀疑是这样的。谢谢,我有点怀疑是这样的。