Verschieben von Zeilen von einer Tabelle in eine andere

9

Ich verschiebe Datensätze als Teil des Archivierungsprozesses von einer Datenbank in eine andere. Ich möchte die Zeilen in die Zieltabelle kopieren und dann dieselben Zeilen aus der Quelltabelle löschen.

Meine Frage ist, was ist der effizienteste Weg, um zu überprüfen, ob die erste Einfügung erfolgreich war, bevor die Zeilen gelöscht wurden.

Meine Idee ist dies, aber ich denke, es gibt einen besseren Weg:

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

Ist es besser / möglich, es mit der RAISERROR-Funktion zu kombinieren? Danke!

Dina
quelle

Antworten:

13

Ich würde die TRY / CATCH- Syntax zusammen mit expliziten Transaktionen empfehlen . Ich gehe für diese Lösung davon aus, dass der Grund für den Einfügefehler eine Art abfangbarer SQL-Fehler ist (z. B. eine Schlüsselverletzung, ein Fehlanpassungs- / Konvertierungsfehler des Datentyps usw.). Die Struktur würde so aussehen:

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

So wie diese Struktur funktioniert: Wenn im INSERT oder im DELETE ein Fehler auftritt, wird die gesamte Aktion zurückgesetzt. Dies garantiert, dass die gesamte Aktion erfolgreich sein muss, um abgeschlossen zu werden. Wenn Sie der Meinung sind, dass dies erforderlich ist, können Sie es mit THROW für 2012 oder RAISERROR für 2008 und früher kombinieren , um zusätzliche Logik hinzuzufügen und einen Rollback zu erzwingen, wenn diese Logik nicht erfüllt wird.

Eine andere Möglichkeit ist, sich SET XACT_ABORT ON anzusehen , obwohl ich der Meinung bin, dass die TRY / CATCH-Syntax Ihnen mehr Granularität bietet.

Mike Fal
quelle
19

Wenn Ihre Archivtabelle dies nicht tut .

  • Haben darauf definierte Trigger aktiviert.
  • Nehmen Sie an beiden Seiten einer FOREIGN KEY-Einschränkung teil.
  • CHECK-Einschränkungen oder aktivierte Regeln haben.

Sie können dies auch in einem Status tun.

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

Dies wird als Einheit entweder erfolgreich sein oder fehlschlagen und vermeidet auch mögliche Rennbedingungen, indem Zeilen zwischen INSERTdem Archiv und hinzugefügt werden DELETE(obwohl Ihre WHEREKlausel dies ohnehin äußerst unwahrscheinlich macht).

Martin Smith
quelle
Nichts des oben Genannten. Ich denke, ich könnte diesen Weg gehen, ich mag Code-Minimalismus. Ich möchte nur keine Datensätze verlieren, wenn die Einfügung aus irgendeinem Grund fehlschlägt. (dh: Tischsperre, Zeitüberschreitungen usw.) Vielen Dank!
Dina
@Dina - Haben Sie angegeben, dass dies mit der OUTPUTKlausel möglich ist? Es ist nicht, weil es nur eine Aussage ist. Vermeidet auch das Problem, die Zeilen zweimal lesen zu müssen (und möglicherweise Zeilen zu verlieren, die zwischen dem Lesen für die Einfügung und dem Lesen für das Löschen hinzugefügt wurden)
Martin Smith
Ja, das habe ich gemeint. Danke, ich verstehe deinen Standpunkt.
Dina
FWIW - Diese Methode bewirkt ein Wachstum der Protokolldatei in der Nähe der Größe der ursprünglichen Tabelle. Stellen Sie sicher, dass Sie damit leben können. Wenn Sie dies nicht können, teilen Sie es mit DELETE TOP (N) und einer While-Schleife, die die Variable @@ rowcount überprüft, in Stapel auf.
Wjdavis5
1

Die Art und Weise, wie ich über die Archivierung nachgedacht habe (was sicher auch nicht perfekt ist), besteht darin, der neuen Archivtabelle eine Bitspalte wie "Archiviert" hinzuzufügen, die nach erfolgreicher Übertragung eines Datensatzes den Wert 1 hätte. Sobald Sie alle Datensätze übertragen haben, können Sie einen Löschvorgang ausführen und gleichzeitig nach dem Feldwert 'Archiviert' von '1' suchen, dh True aus der archivierten Tabelle.

Und ich stimme Mike bei der Verwendung von Try / Catch zu.

Avakharia
quelle
1

Versuche dies:

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
quelle