2009-05-14 5 views
8

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

Есть ли способ, которым я могу это сделать в небольших кусках? Может быть, использовать 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. Выбор TOP 1000 строк, которые соответствуют моим критериям даты в временной таблицу
  2. начать транзакцию
  3. Вставки из температуры таблица в архивную таблицу
  4. Удалить из исходной таблицы, соединяясь с временной таблицей в каждом столбце
  5. Commit transa фикция
  6. Повторить 1-5, пока строки не остаются, которые отвечают критериям даты

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

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

+0

Выводы OUTPUT и INTO - ваш друг, посмотрите их или посмотрите мой ответ ... –

ответ

16

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

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 

Это атомная и последовательны.

+0

Это должен быть принятый ответ. –

0

Как насчет:

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

Как в стороне, это идеальный случай для раздвижного разбиения окон, если вы в состоянии его использовать: http : //weblogs.sqlteam.com/dang/archive/2008/08/30/Sliding-Window-Table-Partitioning.aspx Это переключатель метаданных, поэтому вся загрузка может быть выполнена всего за несколько секунд. –

0

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

INSERT INTO EventsBackups 
SELECT * FROM Events WHERE date criteria 

Позже,

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

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

Ничто из того, что вы сказали до сих пор, говорит о необходимости совершения сделки.

+0

Это слишком ресурсоемкий способ сделать массивную вставку, подобную этой, в очень активной таблице. Он должен быть «помечен», чтобы предотвратить большие ожидания ресурсов. –

+0

Но это таблица резервного копирования, которая будет заблокирована, а не таблица событий. Поэтому блокировка проблемы? Затем вы можете выполнить удаление в кусках, если они находятся в резервной копии. –

+0

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

0

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

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

+0

Я использую SQL 2005, и индексов в таблице вообще нет, что делает инструкции SELECT дорогими. – SqlRyan

0

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

4

использования ВСТАВИТЬ с выходом в статье для хранения идентификаторов вставленных строк, а затем УДАЛИТЬ присоединение к этой временной таблице, чтобы удалить только те идентификаторы

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

Мне нравится это решение. Обратите внимание, что ваш окончательный присоединиться будет: ON Events.PrimaryKey = t.primaryKeyValue , а не ON Events.PrimaryKey = t.YourKeyValue Просто чтобы этот пример соответствует ;-) –

+0

@Aaron Alton, t.YourKeyValue приходит от моего @tempTable, который я определяю в своем коде, нет @TempTable .primaryKeyValue. OUTPUT INSERTED.primaryKeyValue необходимо изменить, чтобы быть INSERTED.his значение ключа. –

+0

Мне тоже нравится это решение, за исключением того, что нет ни одного столбца, который является ключом. В таблице могут быть повторяющиеся строки в одной и той же временной отметке :( Мне действительно это нравится, и это стоит того, чтобы увеличить. – SqlRyan

0

Вот что я в конечном итоге делаю:

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

, если у вас нет ключа, и вы не хотите его добавлять, используйте мой ответ, но измените его , выполните удаление с помощью OUTPUT INTO, просто запишите все столбцы в таблицу temp и затем вставьте из этой таблицы temp. –

+0

Вы должны быть очень осторожны с SQL, который вы опубликовали. Если вы не используете изолированную транзакцию SERIALIZABLE, ваш DELETE не гарантирует, что вы удалите только те строки, которые выбрали SELECT. Посмотрите на неповторяющиеся чтения и фантомные чтения. Если вы поедете с SQL, который вы отправили, единственный способ гарантировать SERIALIZABLE сервером SQL (без индекса в столбце даты) - заблокировать таблицу, что приведет к урону производительности, как никогда ранее не было убито! –

+0

cant Редактировать комментарии * для уровня изоляции Serializable, который должен быть выполнен –

0

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

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

 Смежные вопросы

  • Нет связанных вопросов^_^