Перемещение данных SQL Server в ограниченные (1000 строк) фрагменты - PullRequest
12 голосов
/ 14 мая 2009

Я пишу процесс, который архивирует строки из таблицы SQL Server на основе столбца datetime. Я хочу переместить все строки с датой до X, но проблема в том, что для каждой даты есть миллионы строк, поэтому выполнение BEGIN TRANSACTION ... INSERT ... DELETE ... COMMIT для каждой даты занимает слишком много времени. и блокирует базу данных для других пользователей.

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

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

UPDATE: Еще одним вариантом, который я рассмотрел, было добавление шага:

  1. ВЫБРАТЬ ТОП 1000 строк, которые соответствуют моим критериям даты, во временную таблицу
  2. Начать транзакцию
  3. Вставить из временной таблицы в архивную таблицу
  4. Удалить из исходной таблицы, присоединяясь к временной таблице по каждому столбцу
  5. Подтвердить транзакцию
  6. Повторяйте 1-5, пока не останется ни одной строки, соответствующей критериям даты

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

ДЕТАЛИ: Я использую SQL 2005, так как кто-то спросил.

Ответы [ 8 ]

20 голосов
/ 15 мая 2009

Просто ВСТАВЬТЕ результат УДАЛИТЬ:

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

Это атомарно и согласованно.

4 голосов
/ 14 мая 2009

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

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 
0 голосов
/ 14 мая 2009

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

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

0 голосов
/ 14 мая 2009

Вот что я в итоге сделал:

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, а только 1000, поэтому он соответствующим образом обрабатывает повторы в столбце времени (что меня беспокоило, когда я подумал об использовании ROWCOUNT) Поскольку в столбце времени часто встречаются повторы, я вижу, что он регулярно перемещает 1002 или 1004 строки / итерацию, поэтому я знаю, что он получает все.

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

0 голосов
/ 14 мая 2009

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

0 голосов
/ 14 мая 2009

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

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

0 голосов
/ 14 мая 2009

Как насчет того, чтобы не сделать все это сразу?

INSERT INTO EventsBackups
SELECT * FROM Events WHERE date criteria

Потом позже,

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

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

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

0 голосов
/ 14 мая 2009

Как насчет:

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)
...