Przenoszenie wierszy z jednej tabeli do drugiej

9

Przenoszę rekordy z jednej bazy danych do drugiej w ramach procesu archiwizacji. Chcę skopiować wiersze do tabeli docelowej, a następnie usunąć te same wiersze z tabeli źródłowej.

Moje pytanie brzmi: jaki jest najbardziej efektywny sposób sprawdzenia, czy pierwsza wstawka zakończyła się pomyślnie przed usunięciem wierszy.

Mój pomysł jest taki, ale wydaje mi się, że istnieje lepszy sposób:

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

Czy lepiej / jest możliwe połączenie go z funkcją RAISERROR? Dziękuję Ci!

Dina
źródło

Odpowiedzi:

13

Polecam try / catch składni wraz z jawnych transakcji. Moim założeniem dla tego rozwiązania jest to, że przyczyną niepowodzenia wstawiania jest jakiś możliwy do uchwycenia błąd SQL (taki jak naruszenie klucza, niedopasowanie typu danych / błąd konwersji itp.). Struktura wyglądałaby następująco:

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

Sposób działania tej struktury, jeśli wystąpi błąd w WSTAWIANIE lub USUŃ, cała akcja zostanie wycofana. Gwarantuje to, że cała akcja musi zakończyć się powodzeniem. Jeśli uważasz, że jest to konieczne, możesz połączyć go z THROW na rok 2012 lub RAISERROR w 2008 roku i wcześniej, aby dodać dodatkową logikę i wymusić wycofanie, jeśli ta logika nie została spełniona.

Inną opcją jest przyjrzenie się SET XACT_ABORT ON , choć uważam, że składnia TRY / CATCH zapewnia większą szczegółowość.

Mike Fal
źródło
19

Jeśli tabela archiwum nie .

  • Mają włączone wyzwalacze zdefiniowane na nim.
  • Weź udział po obu stronach ograniczenia klucza OBCEGO.
  • Mają ograniczenia CHECK lub włączone reguły.

Możesz to również zrobić w jednym miejscu.

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

To odniesie sukces lub zakończy się niepowodzeniem jako jednostka, a także pozwoli uniknąć możliwych warunków wyścigu, dodając wiersze między INSERTarchiwum i DELETE(choć WHEREklauzula może i tak uczynić to bardzo mało prawdopodobnym).

Martin Smith
źródło
Żadne z powyższych. Przypuszczam, że mógłbym pójść tą drogą, lubię minimalizm kodu. Po prostu nie chcę stracić rekordów, jeśli wkładka zawiedzie z jakiegokolwiek powodu. (tj .: blokowanie stołu, przekroczenia limitu czasu itp.) Dziękujemy!
Dina
@Dina - Czy wskazałeś, że jest to możliwe dzięki OUTPUTklauzuli? Nie dlatego, że to wszystko jedno zdanie. Pozwala także uniknąć konieczności dwukrotnego czytania wierszy (i możliwej utraty wierszy dodanych między odczytem dla wstawki a odczytem dla usunięcia)
Martin Smith
Tak właśnie miałem na myśli. Dzięki, rozumiem twój punkt widzenia.
Dina
FWIW - ta metoda spowoduje wzrost pliku dziennika zbliżonego do rozmiaru oryginalnej tabeli. Upewnij się, że możesz z tym żyć. Jeśli nie możesz, podziel go na partie za pomocą DELETE TOP (N) i pętli While, która sprawdza zmienną @count rowcount.
Wjdavis5,
1

Sposób, w jaki myślałem o archiwizowaniu (co, jak jestem pewien, też nie jest idealny), polega na dodaniu nieco kolumny do nowej tabeli archiwum, takiej jak „Zarchiwizowane”, która miałaby wartość 1 po pomyślnym przesłaniu rekordu. Po przesłaniu wszystkich rekordów można wykonać operację usuwania, jednocześnie szukając wartości pola „Zarchiwizowane” wynoszącego „1”, tj. True z zarchiwizowanej tabeli.

Zgadzam się z Mike'em w kwestii używania Try / Catch.

avakharia
źródło
1

Spróbuj tego:

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
źródło