我经常在升级脚本中编写如下所示的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。
答案 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