2016-06-30 2 views
1

Я использую следующий скрипт для удаления записей из базы данных небольших партий:обновления Пакетного в 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 

как я могу обновить таблицу в том же манере? У меня нет LIMIT и OFFSET в T-SQL. Я также рассматриваю аспекты производительности.

+0

http://stackoverflow.com/q/1198364/1080354 – gotqn

ответ

3

Вы можете обновить временную таблицу, а затем удалить (или недействить) строки в этой таблице. Что-то вроде этого:

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; 
+0

Неудачно, я не могу удалить, прежде чем обновлять весь набор данных. –

+0

@Алекс Жуковский. , , Вместо этого вы можете установить флаг. –

0

Сделайте это. Это позволит удалить 1000 записей каждые 300 миллисекунд. Но хорошо, что я здесь делаю, заключается в том, что я освобождаю транзакцию и разрешаю другую транзакцию. Потому что может быть другой оператор CRUD. Этот запрос не будет блокировать транзакции, я использую это для DELETE/UPDATE миллионов записей на производственном сервере. Поскольку ответы, которые я вижу, будут по-прежнему блокировать другие транзакции, потому что процесс все равно будет привязан к транзакции, которая вызовет высокий CPU и DISK IO. Кроме того, я установил значение DEADLOCK PRIORITY на низкое, поэтому другая транзакция имеет большее значение. Это займет больше времени. Но это SAFER для серверных затрат и не заблокированных транзакций.

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 
1

Попробуйте это, используйте начальное и конечное значения и приращение его в партиях

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 

выше подхода к работе только тогда, когда у вас есть идентификаторы в последовательности, в противном случае использовать этот подход, чтобы заранее сформировать партии и использовать его, это гарантирует, что каждый раз запись 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 

Надеюсь, это поможет.

+0

Идентификаторы не являются последовательными. Они могут иметь пробелы между –

+0

. Я добавил еще один подход к нескольким идентификаторам –