Database
 sql >> Baza danych >  >> RDS >> Database

Samouczek dotyczący transakcji SQL

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.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Wyzwanie rozpoczęte! Wezwanie społeczności do stworzenia najszybszego generatora serii liczb

  2. Utwórz relację w SQL

  3. Dlaczego warto uczyć się Cassandry z Hadoop?

  4. Czym jest SQL i jak zacząć z nim korzystać?

  5. Jak usunąć kolumnę w SQL?