Déplacement de lignes d'une table à une autre

9

Je déplace des enregistrements d'une base de données à une autre, dans le cadre du processus d'archivage. Je souhaite copier les lignes dans la table de destination, puis supprimer les mêmes lignes de la table source.

Ma question est, quel est le moyen le plus efficace de vérifier si la première insertion a réussi avant de supprimer les lignes.

Mon idée est la suivante, mais je pense qu'il y a une meilleure façon:

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

Est-il préférable / possible de le combiner avec la fonction RAISERROR? Je vous remercie!

Dina
la source

Réponses:

13

Je recommanderais la syntaxe TRY / CATCH avec des transactions explicites. Mon hypothèse pour cette solution est que la raison de l'échec de l'insertion est une sorte d'erreur SQL récupérable (telle qu'une violation de clé, une incompatibilité de type de données / une erreur de conversion, etc.). La structure ressemblerait à ceci:

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

La façon dont cette structure fonctionne, si une erreur se produit dans l'insertion ou la suppression, l'action entière est annulée. Cela garantit que l'action entière doit être réussie pour être achevée. Si vous pensiez que c'était nécessaire, vous pouvez le combiner avec THROW pour 2012 ou RAISERROR en 2008 et précédents pour ajouter une logique supplémentaire et forcer un retour en arrière si cette logique n'était pas respectée.

Une autre option consiste à regarder SET XACT_ABORT ON , bien que je pense que la syntaxe TRY / CATCH vous donne plus de granularité.

Mike Fal
la source
19

Si votre table d'archives ne fonctionne pas .

  • Avoir activé des déclencheurs définis dessus.
  • Participez de chaque côté d'une contrainte FOREIGN KEY.
  • Avoir des contraintes CHECK ou des règles activées.

Vous pouvez également le faire dans un seul état.

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

Cela réussira ou échouera en tant qu'unité et évitera également d'éventuelles conditions de concurrence avec des lignes ajoutées entre les INSERTet l'archive DELETE(bien que votre WHEREclause puisse de toute façon rendre cela extrêmement improbable).

Martin Smith
la source
Aucune de ces réponses. Je suppose que je pourrais suivre cette voie, j'aime le minimalisme du code. Je ne veux tout simplement pas perdre des enregistrements si l'insertion échoue pour une raison quelconque. (c.-à-d. verrouillage de la table, délais d'attente, etc.) Merci!
Dina
@Dina - Avez-vous indiqué que cela était possible avec la OUTPUTclause? Ce n'est pas parce que ce n'est qu'une seule déclaration. Évite également d'avoir à lire deux fois les lignes (et éventuellement à perdre des lignes ajoutées entre la lecture pour l'insertion et la lecture pour la suppression)
Martin Smith
Oui, c'est ce que je voulais dire. Merci, je vois votre point.
Dina
FWIW - cette méthode entraînera une croissance du fichier journal proche de celle de la taille de la table d'origine. Assurez-vous que vous pouvez vivre avec ça. Si vous ne le pouvez pas, divisez-le en lots avec DELETE TOP (N) et une boucle While qui vérifie la variable @@ rowcount.
Wjdavis5
1

La façon dont j'ai pensé à faire de l'archivage (ce qui, je suis sûr, n'est pas parfait non plus), consiste à ajouter une colonne de bits à la nouvelle table d'archives comme `` Archivé '' qui aurait la valeur 1 après le transfert réussi d'un enregistrement. Et une fois que vous avez transféré tous les enregistrements, vous pouvez effectuer une opération de suppression tout en recherchant cette valeur de champ «Archivé» de «1», c'est-à-dire True dans la table archivée.

Et je suis d'accord avec Mike sur l'utilisation de Try / Catch.

avakharia
la source
1

Essaye ça:

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;
Nadeem
la source