Перемещение строк из одной таблицы в другую

9

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

Мой вопрос заключается в том, каков наиболее эффективный способ проверки успешности первой вставки перед удалением строк.

Моя идея такова, но я чувствую, что есть лучший способ:

@num_records=select count(ID) from Source_Table where (criteria for eligible rows)

insert * into Destination_Table where (criteria for eligible rows)

if ((select count(ID) from Destination_Table where (criteria) )=@numrecords)

delete * from Source_Table where (criteria)

Лучше / можно ли сочетать это с функцией RAISERROR? Спасибо!

Dina
источник

Ответы:

13

Я бы порекомендовал синтаксис TRY / CATCH вместе с явными транзакциями. Мое предположение для этого решения состоит в том, что причиной сбоя вставки является какая-то перехватываемая ошибка SQL (например, нарушение ключа, несоответствие типа данных / ошибка преобразования и т. Д.). Структура будет выглядеть так:

BEGIN TRAN

BEGIN TRY
  INSERT INTO foo(col_a,col_b,col_c,recdate)
  SELECT col_a,col_b,col_c,recdate
  FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  DELETE FROM bar
  WHERE recdate BETWEEN @startdate AND @enddate

  COMMIT TRAN
END TRY
BEGIN CATCH
  ROLLBACK TRAN
END CATCH

Как работает эта структура, если в INSERT или DELETE происходит какая-либо ошибка, все действие откатывается. Это гарантирует, что все действие должно быть успешным, чтобы быть завершенным. Если вы считаете, что это необходимо, вы можете объединить его с THROW для 2012 или RAISERROR в 2008 и предыдущих версиях, чтобы добавить дополнительную логику и вызвать откат, если эта логика не была соблюдена.

Другой вариант - посмотреть на SET XACT_ABORT ON , хотя я чувствую, что синтаксис TRY / CATCH дает вам больше детализации.

Майк Фал
источник
19

Если вашей архивной таблицы нет .

  • Включили триггеры, определенные на нем.
  • Участвуйте по обе стороны ограничения FOREIGN KEY.
  • Имейте ограничения ПРОВЕРКИ или включенные правила.

Вы также можете сделать это в одном месте.

DELETE FROM source_table
OUTPUT deleted.Foo,
       deleted.Bar,
       SYSUTCDATETIME()
INTO archive_table(Foo, Bar, archived)
WHERE  Foo = 1; 

Это будет либо успешным, либо неудачным как единое целое, а также позволит избежать возможных условий состязания со строками, добавляемыми между INSERTархивом и DELETE(хотя ваше WHEREпредложение в любом случае может сделать это крайне маловероятным).

Мартин Смит
источник
Ни один из вышеперечисленных. Я полагаю, что могу пойти по этому пути, мне нравится минимализм кода. Я просто не хочу терять записи, если вставка не удалась по любой причине. (т.е. блокировка таблицы, тайм-ауты и т. д.) Спасибо!
Дина
@ Дина - Вы указали, что это возможно с OUTPUTпредложением? Это не потому, что все это одно утверждение. Также избегает проблемы необходимости читать строки дважды (и, возможно, терять строки, которые были добавлены между чтением для вставки и чтением для удаления)
Martin Smith
Да, это то, что я имел в виду. Спасибо, я понимаю вашу точку зрения.
Дина
FWIW - этот метод приведет к увеличению размера файла журнала до размера, соответствующего размеру исходной таблицы. Убедитесь, что вы можете жить с этим. Если вы не можете, разбейте его на пакеты с помощью DELETE TOP (N) и цикла While, который проверяет переменную @@ rowcount.
Wjdavis5
1

Способ, которым я думал о выполнении архивирования (который, я уверен, тоже не идеален), заключается в добавлении небольшого столбца в новую архивную таблицу, например «Archived», который будет иметь значение 1 после успешной передачи записи. И после того, как вы перенесете все записи, вы можете выполнить операцию удаления, одновременно ища для этого значения в поле «Архив» значение «1», т. Е. «Истина» из архивной таблицы.

И я согласен с Майком в использовании Try / Catch.

avakharia
источник
1

Попробуй это:

INSERT dbo.newtable(
      name,
      department,
      Salary
) SELECT 
            name,
            FirstName,
            Lastname
      FROM    (
           DELETE dbo.oldtable
           OUTPUT
                   DELETED.name,
                   DELETED.department,
                   DELETED.Salary
           WHERE ID  IN ( 1001, 1003, 1005 )
      ) AS RowsToMove;

SELECT * FROM dbo.newtable;
SELECT * FROM dbo.oldtable;
Надим
источник