2014-09-03 30 views
6

Mam bazę danych, którą "wypuszczam" co tydzień, co oznacza, że ​​publikuję kopię zapasową, z której mogą zacząć korzystać, i opublikuję skrypt aktualizacyjny, którego mogą użyć do aktualizacji z zeszłego tygodnia (aby mogli zachować swoje bieżące dane, tak bardzo, jak to możliwe). Ten skrypt oczywiście zawiera wiele DDL-CREATE TABLE, ALTER TABLE i tak dalej. Podstawowa struktura to jest tak:Czy instrukcje CREATE TABLE w SQL Server są odporne na ROLLBACK?

/* 

HOW TO USE THIS SCRIPT 

1. Run it against your existing DB 
2. Check whether there were any errors 
3. If there were, issue a rollback by highlighting this: 
     ROLLBACK 
    and executing it 
4. If there weren't, issue a commit by highlighting this: 
     COMMIT 
    and executing it 
5. !!! Not doing either of these will leave a transaction open, which will 
    probably cause all further queries to time out till you do !!! 

*/ 

SET XACT_ABORT ON; 
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

BEGIN TRANSACTION; 
GO 

-- Boilerplate checking and whatnot goes here 
GO 

-- Guts of operation, part 1 
GO 

-- Guts of operation, part 2 
GO 

-- Guts of operation, part 3 
GO 

-- . . . 
GO 

-- Guts of operation, part N 
GO 

-- Boilerplate cleanup stuff here 
GO 

Będziesz pamiętać, że pozostawił otwartą transakcję i powiedział im, aby radzić sobie z jej ukończeniu ręcznie, w zależności od tego, co się stało. Wolałbym, żeby to było automatyczne, ale rozpaczam o tym, biorąc pod uwagę, że całość to zawsze długa seria kilku partii, a bloki TRY nie mogą oczywiście obejmować partii. Tak więc ostatnie dodanie SET XACT_ABORT ON; w celu złagodzenia bólu nieznacznie. Niezależnie od tego, próbowałem tego sam, ponieważ bez niego i nie ma znaczenia dla scenariusza. Tak czy inaczej.

Ostatnio jeden z tych skryptów posiadał instrukcje do tworzenia tabel i innych instrukcji w celu dodania ograniczeń sprawdzających do istniejących tabel. Jeden z moich użytkowników uruchomił skrypt i wystąpił błąd związany z ograniczeniem; Okazuje się, że posiadał wcześniej istniejące dane, które naruszały ograniczenia. OK, nie ma problemu, zrób ROLLBACK. Racja, nie musiał tego robić, XACT_ABORT już to zrobił. Idź i popraw wiersze danych ... gotowe. Teraz, aby spróbować ponownie! Uhp ... co? Tym razem nie ma błędu ograniczenia, ale powoduje to błąd w instrukcjach CREATE TABLE, mówiąc, że tabele już istnieją ...! Huh? Czy to się nie wycofało?

Zakończyliśmy przywracanie z kopii zapasowej i ponowne ustalanie danych i ponowne uruchamianie. Ale to nie jest ani tu, ani tam.

Drodzy czytelnicy: W jaki sposób tworzenie tych tabel przetrwało wycofanie transakcji? Jak mogę je wyeliminować?


EDYCJA: OK, oto przykład, który można uruchomić.

USE tempdb; 
GO 

CREATE DATABASE example; 
GO 

USE example; 
GO 

CREATE TABLE foo (a INT); 
GO 

INSERT INTO foo 
VALUES (100); 
GO 

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

BEGIN TRANSACTION; 
GO 

ALTER TABLE foo ADD CHECK (a < 10);-- Gives error "The ALTER TABLE statement conflicted with the CHECK constraint…", as expected 
GO 

CREATE TABLE bar (b INT); 
GO 

ROLLBACK;-- Gives error "The ROLLBACK TRANSACTION request has no corresponding BEGIN TRANSACTION." Huh? Where did our transaction go? 
GO 

SELECT * 
FROM bar;-- Gives no error. Table still exists! NOT expected! 
GO 

USE tempdb; 
GO 

DROP DATABASE example; 
+0

Nie. Nie są odporne na wycofywanie, trudno się domyślić, co stało się w twoim przypadku z opisu. –

+0

Spróbuj znaleźć najprostszy sposób, w jaki można odtworzyć problem. To pomogłoby nam przeanalizować problem. – Andomar

Odpowiedz

5

SQL Server ma niefortunne zachowanie podczas obsługi błędów. W zależności od konkretnego błędu czasami przerywane jest instrukcja, partia, transakcja i połączenie, lub niektóre, lub żadne. Jest to bardzo podatny na błędy model do zaprogramowania.

W twoim przypadku partia i transakcja zostały przerwane z powodu XACT_ABORT. Ale separatorem wsadowym jest GO. Masz wiele partii. Później partie nadal działają.

Należy użyć jednej partii lub sprawić, by późniejsze partie sprawdzały, czy poprzednia partia była pobierana (może poprzez sprawdzenie @@TRANCOUNT).

(Lepszym modelem dla programu SQL Server byłoby wycofanie transakcji, ale pozostawienie jej otwartej i spowodowanie niepowodzenia wszystkich przyszłych instrukcji, co pozostawi resztę skryptu zawartą w transakcji i niech nie wycieknie. mieć taką funkcję.)

+1

Ach, tak się stało! Tryb SQLcmd i ': przy wyjściu błędu" mogą pomóc. –

+0

Hmm, nie mogę uciec z jedną partią, ponieważ nie przejdzie ona sprawdzania składni obiektów, które jeszcze nie istnieją. Mam do zrobienia 'GO' między, na przykład,' CREATE TABLE' i coś, co robi coś z wynikową tabelą, albo nawet nie spróbuje wykonania.I mam małą nadzieję na wyjaśnienie trybu SQLCmd użytkownikom ...:/ – Atario

+0

@Atario dobrze można zawijać wszystko w try ... catch, a następnie podnieść błąd przerwania połączenia lub 'ustawić noexec on' w bloku catch. –