Movendo linhas de uma tabela para outra


9

Estou movendo registros de um banco de dados para outro, como parte do processo de arquivamento. Quero copiar as linhas para a tabela de destino e excluir as mesmas linhas da tabela de origem.

Minha pergunta é: qual é a maneira mais eficiente de verificar se a primeira inserção foi bem-sucedida antes de excluir as linhas.

Minha idéia é essa, mas sinto que há uma maneira melhor:

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

É melhor / possível combiná-lo com a função RAISERROR? Obrigado!

Respostas:


13

Eu recomendaria a sintaxe TRY / CATCH junto com transações explícitas. Minha suposição para esta solução é que o motivo da falha de inserção é algum tipo de erro SQL interceptável (como violação de chave, incompatibilidade de tipo de dados / erro de conversão, etc.). A estrutura ficaria assim:

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

Da maneira como essa estrutura funciona, se ocorrer algum erro no INSERT ou no DELETE, a ação inteira será revertida. Isso garante que toda a ação seja bem-sucedida para ser concluída. Se você acha que isso é necessário, você pode combiná-lo com o THROW for 2012 ou o RAISERROR em 2008 e anterior para adicionar lógica adicional e forçar uma reversão se essa lógica não for atendida.

Outra opção é examinar SET XACT_ABORT ON , embora eu sinta que a sintaxe TRY / CATCH oferece mais granularidade.


19

Se a sua tabela de arquivamento não .

  • Ativaram gatilhos definidos nele.
  • Participe dos dois lados de uma restrição FOREIGN KEY.
  • Possui restrições CHECK ou regras ativadas.

Você também pode fazer isso de uma só vez.

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

Isso terá êxito ou falhará como uma unidade e também evitará possíveis condições de corrida, com a adição de linhas entre INSERTo arquivo e o arquivo DELETE(embora sua WHEREcláusula possa tornar isso extremamente improvável).


Nenhuma das acima. Suponho que poderia seguir esse caminho, gosto de minimalismo de código. Eu só não quero perder registros se a inserção falhar por qualquer motivo. (ou seja: bloqueio de tabela, tempos limites, etc.) Obrigado!
Dina

@ Dina - Você estava indicando que é possível com a OUTPUTcláusula? Não é porque é tudo uma afirmação. Também evita problema de ter que ler as linhas duas vezes (e, possivelmente, perder linhas que foram adicionadas entre a leitura para a inserção e a leitura para a exclusão)
Martin Smith

Sim, foi isso que eu quis dizer. Obrigado, entendo o seu ponto.
Dina

FWIW - esse método causará um crescimento do arquivo de log próximo ao tamanho da tabela original. Certifique-se de que você pode viver com isso. Se não puder, divida-o em lotes com DELETE TOP (N) e um loop While que verifica a variável @@ rowcount.
Wjdavis5

1

A maneira como pensei em arquivar (que, com certeza, também não é perfeito), é adicionar uma coluna de bits à nova tabela de arquivamento, como 'Arquivado', que teria o valor 1 após a transferência bem-sucedida de um registro. E depois de transferir todos os registros, você pode executar uma operação de exclusão enquanto procura pelo valor do campo 'Arquivado' de '1', ou seja, Verdadeiro da tabela arquivada.

E eu concordo com Mike em usar o Try / Catch.


1

Tente o seguinte:

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;
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.