T-SQL批量更新
Batch update in T-SQL
我正在使用以下脚本从数据库中小批量删除条目:
SET @r = 1;
WHILE @r > 0
BEGIN
DELETE TOP (100000)
[doc].[Document]
WHERE Id IN (SELECT d.Id FROM @documentIds d);
SET @r = @@ROWCOUNT;
END
如何以同样的方式更新 table?我在 T-SQL
中没有 LIMIT
和 OFFSET
。我也在考虑性能方面。
您可以从临时 table 更新,然后删除(或使)table 中的行。像这样:
SET @r = 1;
WHILE @r > 0
BEGIN
UPDATE d
SET col = dd.col
FROM doc.Document d JOIN
(SELECT TOP 10000 FROM @documents dd ORDER BY id) dd
ON d.id = dd.id;
DELETE d TOP 10000 FROM (SELECT TOP 10000 @documents ORDER BY id) d;
SET @r = @@ROWCOUNT;
END;
这样做。这将每 300 毫秒删除 1000 条记录。但我在这里所做的好处是,我释放了交易,并允许另一个交易完成。因为可能还有另一个 CRUD 语句。此查询不会阻止事务,我用它来 DELETE/UPDATE 生产服务器上的数百万条记录。因为我看到的答案仍然会阻止其他事务,因为该进程仍将附加到会导致高 CPU 和磁盘 IO 的事务。此外,我将 DEADLOCK PRIORITY 设置为低,以便另一个事务具有更高的重要性。它确实需要更长的时间。但这对于服务器成本来说是更安全的,并且不会阻止交易。
SET DEADLOCK_PRIORITY -10
DECLARE @r = 1;
WHILE @r > 0 > 0
BEGIN
DELETE TOP (1000)
[doc].[Document]
WHERE Id IN (SELECT d.Id FROM @documentIds d);
SET @r = @@ROWCOUNT;
WAITFOR DELAY '00:00:00.300'
END
试试这个,使用开始值和结束值并分批递增
DECLARE @Batch INT
,@StartId BIGINT
,@EndId BIGINT
,@r INT
SELECT @Batch = 10000
,@StartId = 1
,@EndId = 0
,@r = 1
WHILE @r > 0
BEGIN
SET @StartId = @EndId + 1
SET @EndId = @EndId + @Batch
UPDATE d
SET col = dd.col
FROM doc.Document d
INNER JOIN @documents dd ON d.id = dd.id
AND dd.id BETWEEN @StartId AND @EndId
SET @r = @@ROWCOUNT
END
以上方法只有当你有顺序的 id 时才有效,否则使用这种方法预先生成批次并使用它,这将确保每次更新 10000 条记录。
DECLARE @Batch INT
,@StartId BIGINT
,@EndId BIGINT
,@Cnt INT
,@TotalIds INT
DECLARE @Docs TABLE
(
StartId BIGINT,
EndId BIGINT,
BatchID INT
)
SELECT @Batch = 10000
,@StartId = 1
,@EndId = 0
,@Cnt = 1
,@TotalIds = 0
;WITH CTE_Docs AS
( SELECT TOP (100) PERCENT id, ROW_NUMBER() OVER (ORDER BY id) as RowID -- Give seq numbers to each row
FROM @documentIds d
)
-- create batches and batch start and end point
INSERT INTO @Docs(StartId, EndId, BatchId )
SELECT MIN(id) StartID,
MAX(id) EndID,
(RowID/@Batch)+1 AS BatchID
FROM CTE_Docs
GROUP BY RowID/@Batch
ORDER BY BatchID
-- get counter to loop through
SELECT @TotalIds = MAX(BatchID)
FROM @Docs
WHILE @Cnt <= @TotalIds BEGIN
SELECT @StartID = StartID,
@EndID = EndID
FROM @Docs
WHERE BatchID = @Cnt
UPDATE d
SET col = dd.col
FROM doc.Document d
INNER JOIN @documents dd ON d.id = dd.id
AND dd.id BETWEEN @StartId AND @EndId
SET @Cnt = @Cnt + 1
END
希望对您有所帮助。
我正在使用以下脚本从数据库中小批量删除条目:
SET @r = 1;
WHILE @r > 0
BEGIN
DELETE TOP (100000)
[doc].[Document]
WHERE Id IN (SELECT d.Id FROM @documentIds d);
SET @r = @@ROWCOUNT;
END
如何以同样的方式更新 table?我在 T-SQL
中没有 LIMIT
和 OFFSET
。我也在考虑性能方面。
您可以从临时 table 更新,然后删除(或使)table 中的行。像这样:
SET @r = 1;
WHILE @r > 0
BEGIN
UPDATE d
SET col = dd.col
FROM doc.Document d JOIN
(SELECT TOP 10000 FROM @documents dd ORDER BY id) dd
ON d.id = dd.id;
DELETE d TOP 10000 FROM (SELECT TOP 10000 @documents ORDER BY id) d;
SET @r = @@ROWCOUNT;
END;
这样做。这将每 300 毫秒删除 1000 条记录。但我在这里所做的好处是,我释放了交易,并允许另一个交易完成。因为可能还有另一个 CRUD 语句。此查询不会阻止事务,我用它来 DELETE/UPDATE 生产服务器上的数百万条记录。因为我看到的答案仍然会阻止其他事务,因为该进程仍将附加到会导致高 CPU 和磁盘 IO 的事务。此外,我将 DEADLOCK PRIORITY 设置为低,以便另一个事务具有更高的重要性。它确实需要更长的时间。但这对于服务器成本来说是更安全的,并且不会阻止交易。
SET DEADLOCK_PRIORITY -10
DECLARE @r = 1;
WHILE @r > 0 > 0
BEGIN
DELETE TOP (1000)
[doc].[Document]
WHERE Id IN (SELECT d.Id FROM @documentIds d);
SET @r = @@ROWCOUNT;
WAITFOR DELAY '00:00:00.300'
END
试试这个,使用开始值和结束值并分批递增
DECLARE @Batch INT
,@StartId BIGINT
,@EndId BIGINT
,@r INT
SELECT @Batch = 10000
,@StartId = 1
,@EndId = 0
,@r = 1
WHILE @r > 0
BEGIN
SET @StartId = @EndId + 1
SET @EndId = @EndId + @Batch
UPDATE d
SET col = dd.col
FROM doc.Document d
INNER JOIN @documents dd ON d.id = dd.id
AND dd.id BETWEEN @StartId AND @EndId
SET @r = @@ROWCOUNT
END
以上方法只有当你有顺序的 id 时才有效,否则使用这种方法预先生成批次并使用它,这将确保每次更新 10000 条记录。
DECLARE @Batch INT
,@StartId BIGINT
,@EndId BIGINT
,@Cnt INT
,@TotalIds INT
DECLARE @Docs TABLE
(
StartId BIGINT,
EndId BIGINT,
BatchID INT
)
SELECT @Batch = 10000
,@StartId = 1
,@EndId = 0
,@Cnt = 1
,@TotalIds = 0
;WITH CTE_Docs AS
( SELECT TOP (100) PERCENT id, ROW_NUMBER() OVER (ORDER BY id) as RowID -- Give seq numbers to each row
FROM @documentIds d
)
-- create batches and batch start and end point
INSERT INTO @Docs(StartId, EndId, BatchId )
SELECT MIN(id) StartID,
MAX(id) EndID,
(RowID/@Batch)+1 AS BatchID
FROM CTE_Docs
GROUP BY RowID/@Batch
ORDER BY BatchID
-- get counter to loop through
SELECT @TotalIds = MAX(BatchID)
FROM @Docs
WHILE @Cnt <= @TotalIds BEGIN
SELECT @StartID = StartID,
@EndID = EndID
FROM @Docs
WHERE BatchID = @Cnt
UPDATE d
SET col = dd.col
FROM doc.Document d
INNER JOIN @documents dd ON d.id = dd.id
AND dd.id BETWEEN @StartId AND @EndId
SET @Cnt = @Cnt + 1
END
希望对您有所帮助。