W SQL transakcje służą do zachowania integralności danych poprzez zapewnienie, że sekwencja instrukcji SQL zostanie wykonana całkowicie lub wcale.
Transakcje zarządzają sekwencjami instrukcji SQL, które muszą być wykonane jako pojedyncza jednostka pracy, tak aby baza danych nigdy nie zawierała wyników operacji częściowych.
Gdy transakcja wprowadza wiele zmian w bazie danych, albo wszystkie zmiany kończą się powodzeniem po zatwierdzeniu transakcji, albo wszystkie zmiany są cofane po wycofaniu transakcji.
Kiedy używać transakcji?
Transakcje mają kluczowe znaczenie w sytuacjach, w których integralność danych byłaby zagrożona w przypadku niepowodzenia którejkolwiek z sekwencji instrukcji SQL.
Na przykład, jeśli przenosisz pieniądze z jednego konta bankowego na drugie, musisz odliczyć pieniądze z jednego konta i dodać je do drugiego. Nie chciałbyś, aby upadło w połowie, w przeciwnym razie pieniądze mogą zostać pobrane z jednego konta, ale nie zapisane na drugim.
Możliwe przyczyny niepowodzenia mogą obejmować niewystarczające środki, nieprawidłowy numer konta, awarię sprzętu itp.
Więc nie chcesz znaleźć się w sytuacji, w której pozostaje tak:
Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
To byłoby naprawdę zły. Baza danych zawierałaby niespójne dane, a pieniądze rozpłynęłyby się w powietrzu. Wtedy bank straciłby klienta (bank prawdopodobnie straciłby wszystkich swoich klientów, gdyby tak się działo), a Ty stracisz pracę.
Aby uratować swoją pracę, możesz użyć transakcji, która wyglądałaby mniej więcej tak:
START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION
Możesz napisać logikę warunkową wewnątrz tej transakcji, która wycofuje transakcję, jeśli coś pójdzie nie tak.
Na przykład, jeśli coś pójdzie nie tak między kontem obciążającym 1 a kontem kredytowym 2, cała transakcja zostanie wycofana.
Dlatego byłyby tylko dwa możliwe wyniki:
Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Lub:
Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)
Jest to uproszczony obraz, ale jest to klasyczna ilustracja działania transakcji SQL. Transakcje SQL mają ACID.
Typy transakcji
Transakcje SQL można uruchamiać w następujących trybach.
Tryb transakcji | Opis |
---|---|
Automatycznie zatwierdzaj transakcję | Każdy indywidualny wyciąg jest transakcją. |
Transakcja niejawna | Nowa transakcja jest niejawnie uruchamiana po zakończeniu poprzedniej transakcji, ale każda transakcja jest jawnie zakończona, zwykle z COMMIT lub ROLLBACK oświadczenie w zależności od DBMS. |
Jawna transakcja | Rozpoczęty wyraźnie od linii, takiej jak START TRANSACTION , START TRANSACTION lub podobne, w zależności od DBMS i jawnie zatwierdzone lub wycofane z odpowiednimi instrukcjami. |
Transakcja o zakresie wsadowym | Dotyczy tylko wielu aktywnych zestawów wyników (MARS). Transakcja jawna lub niejawna, która rozpoczyna się w ramach sesji MARS, staje się transakcją o zakresie wsadowym. |
Dokładne dostępne tryby i opcje mogą zależeć od DBMS. Ta tabela przedstawia tryby transakcji dostępne w SQL Server.
W tym artykule koncentrujemy się głównie na transakcjach jawnych.
Zobacz, jak niejawne transakcje działają w SQL Server, aby omówić różnicę między niejawnymi transakcjami a automatycznym zatwierdzaniem.
Składnia
Poniższa tabela przedstawia podstawową składnię rozpoczynania i kończenia jawnej transakcji w niektórych z bardziej popularnych DBMS.
SZBD | Składnia jawnej transakcji |
---|---|
MySQL, MariaDB, PostgreSQL | Transakcje jawne zaczynają się od START TRANSACTION lub BEGIN oświadczenie. COMMIT zatwierdza bieżącą transakcję, czyniąc jej zmiany trwałymi. ROLLBACK cofa bieżącą transakcję, anulując jej zmiany. |
SQLite | Jawne transakcje zaczynają się od BEGIN TRANSACTION oświadczenie i zakończ COMMIT lub ROLLBACK oświadczenie. Może również kończyć się na END oświadczenie. |
Serwer SQL | Jawne transakcje zaczynają się od BEGIN TRANSACTION oświadczenie i zakończ COMMIT lub ROLLBACK oświadczenie. |
Wyrocznia | Transakcje jawne zaczynają się od SET TRANSACTION oświadczenie i zakończ COMMIT lub ROLLBACK oświadczenie. |
W wielu przypadkach niektóre słowa kluczowe są opcjonalne, gdy używane są transakcje jawne. Na przykład w SQL Server i SQLite możesz po prostu użyć BEGIN
(zamiast START TRANSACTION
) i/lub możesz zakończyć z COMMIT TRANSACTION
(w przeciwieństwie do COMMIT
).
Istnieje również wiele innych słów kluczowych i opcji, które możesz określić podczas tworzenia transakcji, więc zapoznaj się z dokumentacją DBMS, aby uzyskać pełną składnię.
Przykład transakcji SQL
Oto przykład prostej transakcji w SQL Server:
BEGIN TRANSACTION
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;
W takim przypadku informacje o zamówieniu są usuwane z dwóch tabel. Oba stwierdzenia są traktowane jako jedna jednostka pracy.
Możemy zapisać logikę warunkową w naszej transakcji, aby w przypadku błędu została wycofana.
Nazywanie transakcji
Niektóre DBMS pozwalają na nadanie nazwy Twoim transakcjom. W SQL Server możesz dodać wybraną nazwę po BEGIN
i COMMIT
oświadczenia.
BEGIN TRANSACTION MyTransaction
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;
Przykład przywracania transakcji SQL 1
Oto znowu poprzedni przykład, ale z dodatkowym kodem. Dodatkowy kod służy do wycofania transakcji w przypadku błędu.:
BEGIN TRANSACTION MyTransaction
BEGIN TRY
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION MyTransaction
END CATCH
TRY...CATCH
instrukcja implementuje obsługę błędów w SQL Server. Możesz zawrzeć dowolną grupę instrukcji T-SQL w TRY
blok. Następnie, jeśli wystąpi błąd w TRY
blok, kontrola jest przekazywana do innej grupy instrukcji, która jest zawarta w CATCH
blokować.
W tym przypadku używamy CATCH
blok, aby wycofać transakcję. Biorąc pod uwagę, że znajduje się w CATCH
blok, cofanie następuje tylko w przypadku błędu.
Przykład wycofania transakcji SQL 2
Przyjrzyjmy się bliżej bazie danych, z której właśnie usunęliśmy wiersze.
W poprzednim przykładzie usunęliśmy wiersze z Orders
i OrderItems
tabele w następującej bazie danych:
W tej bazie danych, za każdym razem, gdy klient składa zamówienie, w Orders
wstawiany jest wiersz tabeli i jeden lub więcej wierszy w OrderItems
stół. Liczba wierszy wstawionych do OrderItems
zależy od tego, ile różnych produktów zamawia klient.
Ponadto, jeśli jest to nowy klient, nowy wiersz jest wstawiany do Customers
tabela.
W takim przypadku wiersze należy wstawić do trzech tabel.
W przypadku awarii nie chcielibyśmy, aby wiersz był wstawiany do Orders
tabeli, ale brak odpowiednich wierszy w OrderItems
stół. Spowodowałoby to zamówienie bez żadnych pozycji zamówienia. Zasadniczo chcemy, aby obie tabele były całkowicie zaktualizowane lub wcale.
Tak samo było, gdy usunęliśmy wiersze. Chcieliśmy usunąć wszystkie wiersze lub wcale.
W SQL Server moglibyśmy napisać następującą transakcję dla INSERT
oświadczenia.
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
W tym przykładzie założono, że gdzie indziej istnieje logika, która określa, czy klient już istnieje w bazie danych.
Klient mógł zostać wstawiony poza tą transakcją:
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
Gdyby transakcja się nie powiodła, klient nadal byłby w bazie (ale bez żadnych zamówień). Aplikacja musiałaby sprawdzić, czy klient już istnieje przed wykonaniem transakcji.
Transakcja SQL z punktami zapisu
Punkt zapisu definiuje lokalizację, do której transakcja może powrócić, jeśli część transakcji zostanie warunkowo anulowana. W SQL Server określamy punkt zapisu za pomocą SAVE TRANSACTION savepoint_name
(gdzie savepoint_name to nazwa, którą nadajemy punktowi zapisu).
Przepiszmy poprzedni przykład, aby uwzględnić punkt zapisu:
BEGIN TRANSACTION
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;
Tutaj ustawiliśmy punkt zapisu bezpośrednio po kliencie INSERT
oświadczenie. W dalszej części transakcji korzystam z funkcji ROLLBACK
instrukcja, aby poinstruować transakcję, aby cofnęła się do tego punktu zapisu.
Kiedy uruchamiam to zestawienie, klient jest wstawiany, ale żadne informacje o zamówieniu nie są wstawiane.
Jeśli transakcja zostanie wycofana do punktu zapisu, musi przejść do zakończenia z większą liczbą instrukcji SQL, jeśli to konieczne, i COMMIT TRANSACTION
wyciągu lub należy go całkowicie anulować, cofając całą transakcję.
Jeśli przeniosę ROLLBACK
instrukcja powrót do poprzedniego INSERT
oświadczenie, takie jak:
BEGIN TRANSACTION
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;
Powoduje to błąd konfliktu klucza obcego. W szczególności pojawia się następujący błąd:
(1 row affected) (1 row affected) (1 row affected) Msg 547, Level 16, State 0, Line 13 The INSERT statement conflicted with the FOREIGN KEY constraint "FK_OrderItems_Orders". The conflict occurred in database "KrankyKranes", table "dbo.Orders", column 'OrderId'. The statement has been terminated. (1 row affected)
Stało się tak, ponieważ mimo że zamówienie zostało już wstawione, ta operacja została cofnięta, gdy cofnęliśmy się do punktu zapisu. Następnie transakcja dobiegła końca. Ale kiedy napotkał ostatni element zamówienia, nie było odpowiadającego zamówienia (ponieważ zostało cofnięte) i otrzymaliśmy błąd.
Kiedy sprawdzam bazę danych, klient został wstawiony, ale znowu nie wstawiono żadnych informacji o zamówieniu.
W razie potrzeby możesz odwołać się do tego samego punktu zapisu z wielu miejsc w transakcji.
W praktyce użyjesz programowania warunkowego, aby zwrócić transakcję do punktu zapisu.
Zagnieżdżone transakcje
W razie potrzeby możesz także zagnieżdżać transakcje w innych transakcjach.
Tak:
BEGIN TRANSACTION Transaction1;
UPDATE table1 ...;
BEGIN TRANSACTION Transaction2;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRANSACTION Transaction2;
UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;
Jak wspomniano, dokładna składnia, której użyjesz do utworzenia transakcji, będzie zależeć od Twojego DBMS, więc sprawdź dokumentację swojego DBMS, aby uzyskać pełny obraz opcji podczas tworzenia transakcji w SQL.