Spostamento di righe da una tabella all'altra


9

Sto spostando i record da un database all'altro, come parte del processo di archiviazione. Voglio copiare le righe nella tabella di destinazione e quindi eliminare le stesse righe dalla tabella di origine.

La mia domanda è: qual è il modo più efficiente per verificare se il primo inserimento ha avuto esito positivo prima di eliminare le righe.

La mia idea è questa, ma sento che c'è un modo migliore:

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

È meglio / possibile combinarlo con la funzione RAISERROR? Grazie!

Risposte:


13

Consiglierei la sintassi TRY / CATCH insieme a transazioni esplicite. La mia ipotesi per questa soluzione è che il motivo dell'insuccesso dell'inserimento è una sorta di errore SQL intercettabile (come una violazione della chiave, una mancata corrispondenza del tipo di dati / errore di conversione, ecc.). La struttura sarebbe simile a questa:

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

Il modo in cui funziona questa struttura, se si verifica un errore in INSERT o DELETE, viene eseguito il rollback dell'intera azione. Ciò garantisce che l'intera azione debba essere completata con successo. Se ritieni che fosse necessario, puoi combinarlo con THROW per il 2012 o RAISERROR nel 2008 e in precedenza per aggiungere ulteriore logica e forzare un rollback se tale logica non fosse soddisfatta.

Un'altra opzione è guardare SET XACT_ABORT ON , anche se ritengo che la sintassi TRY / CATCH ti dia più granularità.


19

Se la tabella dell'archivio no .

  • Hanno attivato i trigger definiti su di esso.
  • Partecipa su entrambi i lati di un vincolo FOREIGN KEY.
  • Avere vincoli CHECK o regole abilitate.

Potresti anche farlo in un solo stato.

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

Ciò avrà esito positivo o negativo come unità ed evita anche possibili condizioni di gara con l'aggiunta di righe tra INSERTl'archivio e DELETE(anche se la tua WHEREclausola potrebbe rendere ciò estremamente improbabile comunque).


Nessuno dei precedenti. Suppongo che potrei seguire quella strada, mi piace il minimalismo del codice. Non voglio perdere i record se l'inserimento non riesce per qualsiasi motivo. (ad esempio: blocco della tabella, timeout, ecc.) Grazie!
Dina,

@Dina - Stavi indicando che è possibile con la OUTPUTclausola? Non è perché è tutta un'affermazione. Evita anche il problema di dover leggere le righe due volte (e possibilmente perdere le righe che sono state aggiunte tra la lettura per l'inserimento e la lettura per l'eliminazione)
Martin Smith

Sì, questo è ciò che intendevo. Grazie, vedo il tuo punto.
Dina,

FWIW: questo metodo causerà una crescita del file di registro vicina a quella delle dimensioni della tabella originale. Assicurati di poter convivere con quello. Se non è possibile, suddividerlo in batch con DELETE TOP (N) e un ciclo While che controlla la variabile @@ rowcount.
Wjdavis5,

1

Il modo in cui ho pensato di fare l'archiviazione (che sicuramente non è perfetto) è quello di aggiungere una colonna di bit alla nuova tabella di archivio come 'Archiviato' che avrebbe il valore di 1 dopo il trasferimento riuscito di un record. E una volta trasferiti tutti i record, è possibile eseguire un'operazione di eliminazione cercando anche questo valore del campo "Archiviato" di "1", ovvero True dalla tabella archiviata.

E sono d'accordo con Mike sull'utilizzo di Try / Catch.


1

Prova questo:

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.