Aby wyjaśnić znaczenie atrybutu stwórzmy najpierw tabelę składają się wyłącznie z jednej kolumny – klucza głównego. Ponadto nie ustawiajmy IDENTITY dla tej kolumny. Następnie spróbujmy wykonać 2 poniższe insert’y:
begin transaction insert into TestSet (ID) values(1); insert into TestSet (ID) values(1); commit transaction;
Przy drugim insercie wyskoczy błąd. To jest oczywiste ponieważ klucz główny musi być unikalny. Jeśli jednak odpalimy select’a przekonamy się, że wartości z pierwszego insert’a zostały wstawione (1). Dzieje się tak ponieważ domyślnie transakcja nie jest anulowana w przypadku wystąpienia błędu. Aby temu zaradzić należy ustawić właśnie zmienną XACT_ABORT na true:
SET XACT_ABORT on go begin transaction insert into TestSet (ID) values(1); insert into TestSet (ID) values(1); commit transaction;
XACT_ABORT powoduje, że w przypadku wystąpienia błędu cała transakcja jest anulowana (rollback). Do bazy nie zostanie dodany żaden wpis – nawet pierwszy insert, który nie powoduje błędu.
I to jest właśnie duży minus MS SQL! Gdy jakaś zapytanie w ciąg odpalanych instrukcji spowoduje błąd to instrukcje wykonują się do końca. Powinno zostać przerwane oraz cofnięte. Np. gdy mamy procedure, a w niej jakiś ciąg instrukcji. To gdy w środku jedno z zapytań spowoduje błąd, dalsze instrukcje zostaną wykonane, a całość prócz błędnego zapytania zostanie zatwierdzona – masakra;)
–Gwoli wyklarowania tematu:
–zalecana metoda obslugi transakcji jest
begin transaction
BEGIN TRY
insert into TestSet (ID) values(1);
insert into TestSet (ID) values(2);
commit transaction;
print ‘all ok!!!!’
END TRY
BEGIN CATCH
print ‘exception!!!!’
rollback transaction;
END CATCH
–wtedy wszystko dziala pieknie bez XACT_ABORT
–Gwoli wyklarowania tematu:
–zalecana metoda obslugi transakcji jest:
begin transaction
BEGIN TRY
insert into TestSet (ID) values(1);
insert into TestSet (ID) values(2);
commit transaction;
print ‘all ok!!!!’
END TRY
BEGIN CATCH
print ‘exception!!!!’
rollback transaction;
END CATCH
–wtedy wszystko dziala pieknie bez XACT_ABORT
Oczywiscie druga linia insertu powinna byc:
insert into TestSet (ID) values(1);
@Koto: oczywiście masz racje ale celem post’a było przedstawienie atrybutu a nie obsługa transakcji.
Pozdrawiam,
Piotr Zielinski
Poza powyższym – nie wszystkie błędy są wyłapywane przez begin catch end catch.