Перемещение данных SQL Server в ограниченных (1000 строк) кусках

Я пишу процесс, который архивирует строки из таблицы SQL Server на основе столбца datetime. Я хочу переместить все строки с датой до X, но проблема в том, что для каждой даты есть миллионы строк, поэтому делаем транзакцию BEGIN...ВСТАВЛЯТЬ...УДАЛИТЬ...Фиксация для каждой даты занимает слишком много времени и блокирует базу данных для других пользователей.

есть ли способ сделать это меньшими кусками? Может быть, используя ROWCOUNT или что-то в этом роде?

Я изначально считать что-то вроде этого:

SET ROWCOUNT 1000

DECLARE @RowsLeft DATETIME
DECLARE @ArchiveDate DATETIME

SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate)

WHILE @ROWSLEFT IS NOT NULL
BEGIN

    INSERT INTO EventsBackups
    SELECT top 1000 * FROM Events

    DELETE Events

    SET @ROWSLEFT = (SELECT TOP 1 dtcol FROM Events WHERE dtcol <= @ArchiveDate)

END

но потом я понял, что я не могу гарантировать, что строки Я удалю это те, что я попятился. Или могу...?

обновление: Другие варианты я бы рассмотрел еще на шаг:

  1. выберите верхние 1000 строк, которые соответствуют моим критериям даты в временную таблицу
  2. Начать Транзакцию
  3. вставить из таблицы temp в таблицу архива
  4. удалить из источника таблица, присоединение к таблице temp через каждый столбец
  5. совершают сделки
  6. повторите 1-5, пока не останется строк, соответствующих критериям даты

есть ли у кого-нибудь идея о том, как расход этой серии может сравниться с некоторыми другими вариантами, обсуждаемыми ниже?

деталь: Я использую SQL 2005, так как кто-то спросил.

8 ответов


просто вставьте результат удаления:

WHILE 1=1
BEGIN

    WITH EventsTop1000 AS (
    SELECT TOP 1000 * 
        FROM Events
      WHERE <yourconditionofchoice>)
    DELETE EventsTop1000
        OUTPUT DELETED.* 
        INTO EventsBackup;

    IF (@@ROWCOUNT = 0)
        BREAK;
END

это атомарный и последовательный.


используйте INSERT с предложением OUTPUT INTO для хранения идентификаторов вставленных строк, а затем удалите присоединение к этой временной таблице, чтобы удалить только эти идентификаторы

DECLARE @TempTable (YourKeyValue KeyDatatype not null)

INSERT INTO EventsBackups
    (columns1,column2, column3)
    OUTPUT INSERTED.primaryKeyValue
    INTO @TempTable
    SELECT
        top 1000
        columns1,column2, column3
        FROM Events

DELETE Events
    FROM Events
        INNER JOIN @TempTable  t ON Events.PrimaryKey=t.YourKeyValue 

Как насчет:

INSERT INTO EventsBackups
SELECT TOP 1000 * FROM Events ORDER BY YourKeyField

DELETE Events
WHERE YourKeyField IN (SELECT TOP 1000 YourKeyField FROM Events ORDER BY YourKeyField)

Как насчет не делать все сразу?

INSERT INTO EventsBackups
SELECT * FROM Events WHERE date criteria

потом

DELETE FROM Events
SELECT * FROM Events INNER JOIN EventsBackup on Events.ID = EventsBackup.ID

или эквивалент.

ничто из того, что вы сказали до сих пор, не предполагает, что вам нужна транзакция.


У вас есть индекс на поле? Если у вас нет sql, может быть вынужден перейти на блокировку таблицы, которая заблокирует всех ваших пользователей во время выполнения инструкций архива.

Я думаю, вам понадобится индекс для этой операции, чтобы выполнить все хорошо! Поставить индекс на поле даты и повторите операцию!


не могли бы вы сделать копию события, переместить все строки с датами >= x к этому, отбросить события и переименовать события копирования? Или скопировать, обрезать и затем скопировать обратно? Если вы можете позволить себе немного простоя, это, вероятно, будет самый быстрый подход.


вот что я делал:

SET @CleanseFilter = @startdate
WHILE @CleanseFilter IS NOT NULL
BEGIN
    BEGIN TRANSACTION

        INSERT INTO ArchiveDatabase.dbo.MyTable
        SELECT *
          FROM dbo.MyTable
         WHERE startTime BETWEEN @startdate AND @CleanseFilter

        DELETE dbo.MyTable
         WHERE startTime BETWEEN @startdate AND @CleanseFilter

    COMMIT TRANSACTION

    SET @CleanseFilter = (SELECT MAX(starttime)
                FROM (SELECT TOP 1000
                             starttime
                    FROM dbo.MyTable
                       WHERE startTime BETWEEN @startdate AND @enddate
                    ORDER BY starttime) a)
END

Я не вытягиваю ровно 1000, просто 1000ish, поэтому он обрабатывает повторения в столбце времени соответствующим образом (что-то, о чем я беспокоился, когда я рассматривал использование ROWCOUNT). Поскольку в столбце time часто повторяются повторы, я вижу, что он регулярно перемещает 1002 или 1004 строки/итерации, поэтому я знаю, что он получает все.

Я представляю это как ответ, чтобы его можно было судить по другим решениям, которые люди предоставили. Позволить мне знайте, есть ли что-то явно неправильное в этом методе. Спасибо всем за помощь, и я приму тот ответ, за который проголосует больше всех через несколько дней.


другой вариант-добавить процедуру запуска в таблицу событий, которая ничего не делает, кроме добавления той же записи в таблицу EventsBackup.

таким образом, EventsBackup всегда обновлен, и все, что вы делаете, это периодически очищать записи из таблицы событий.