In SQL werden Transaktionen verwendet, um die Datenintegrität aufrechtzuerhalten, indem sichergestellt wird, dass eine Folge von SQL-Anweisungen vollständig oder gar nicht ausgeführt wird.
Transaktionen verwalten Sequenzen von SQL-Anweisungen, die als einzelne Arbeitseinheit ausgeführt werden müssen, sodass die Datenbank niemals die Ergebnisse von Teiloperationen enthält.
Wenn eine Transaktion mehrere Änderungen an der Datenbank vornimmt, sind entweder alle Änderungen erfolgreich, wenn die Transaktion festgeschrieben wird, oder alle Änderungen werden rückgängig gemacht, wenn die Transaktion zurückgesetzt wird.
Wann sollte eine Transaktion verwendet werden?
Transaktionen sind in Situationen von größter Bedeutung, in denen die Datenintegrität gefährdet wäre, falls eine beliebige Folge von SQL-Anweisungen fehlschlagen würde.
Wenn Sie beispielsweise Geld von einem Bankkonto auf ein anderes überweisen, müssten Sie Geld von einem Konto abziehen und es dem anderen hinzufügen. Sie möchten nicht, dass es auf halbem Weg fehlschlägt, da sonst Geld von einem Konto abgebucht, aber dem anderen nicht gutgeschrieben werden könnte.
Mögliche Gründe für das Scheitern können unzureichendes Guthaben, ungültige Kontonummer, ein Hardwarefehler usw. sein.
Also nicht in einer Situation sein wollen, in der es so bleibt:
Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Das wäre wirklich Schlecht. Die Datenbank hätte inkonsistente Daten und Geld würde sich in Luft auflösen. Dann würde die Bank einen Kunden verlieren (die Bank würde wahrscheinlich alle ihre Kunden verlieren, wenn dies so weiterginge) und Sie würden Ihren Job verlieren.
Um Ihren Job zu speichern, könnten Sie eine Transaktion verwenden, die etwa so ablaufen würde:
START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION
Sie könnten eine bedingte Logik in diese Transaktion schreiben, die die Transaktion zurücksetzt, wenn etwas schief geht.
Wenn beispielsweise etwas zwischen dem Belastungskonto 1 und dem Gutschriftskonto 2 schief geht, wird die gesamte Transaktion rückgängig gemacht.
Daher gäbe es nur zwei mögliche Ergebnisse:
Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Oder:
Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)
Dies ist eine vereinfachte Darstellung, aber es ist eine klassische Veranschaulichung der Funktionsweise von SQL-Transaktionen. SQL-Transaktionen haben ACID.
Transaktionstypen
SQL-Transaktionen können in den folgenden Modi ausgeführt werden.
Transaktionsmodus | Beschreibung |
---|---|
Autocommit-Transaktion | Jeder einzelne Auszug ist eine Transaktion. |
Implizite Transaktion | Eine neue Transaktion wird implizit gestartet, wenn die vorherige Transaktion abgeschlossen ist, aber jede Transaktion wird explizit abgeschlossen, typischerweise mit einem COMMIT oder ROLLBACK Anweisung je nach DBMS. |
Explizite Transaktion | Explizit gestartet mit einer Zeile wie START TRANSACTION , BEGIN TRANSACTION oder ähnliches, abhängig vom DBMS, und mit den entsprechenden Anweisungen explizit festgeschrieben oder zurückgesetzt. |
Batch-bezogene Transaktion | Gilt nur für mehrere aktive Ergebnissätze (MARS). Eine explizite oder implizite Transaktion, die unter einer MARS-Sitzung beginnt, wird zu einer Batch-bezogenen Transaktion. |
Die genauen verfügbaren Modi und Optionen können vom DBMS abhängen. Diese Tabelle beschreibt die in SQL Server verfügbaren Transaktionsmodi.
In diesem Artikel konzentrieren wir uns hauptsächlich auf explizite Transaktionen.
Unter Funktionsweise impliziter Transaktionen in SQL Server finden Sie eine Erläuterung des Unterschieds zwischen impliziten Transaktionen und Autocommit.
Syntax
Die folgende Tabelle skizziert die grundlegende Syntax zum Starten und Beenden einer expliziten Transaktion in einigen der bekannteren DBMSs.
DBMS | Explizite Transaktionssyntax |
---|---|
MySQL, MariaDB, PostgreSQL | Explizite Transaktionen beginnen mit START TRANSACTION oder BEGIN Erklärung. COMMIT schreibt die aktuelle Transaktion fest und macht ihre Änderungen dauerhaft. ROLLBACK setzt die aktuelle Transaktion zurück und verwirft ihre Änderungen. |
SQLite | Explizite Transaktionen beginnen mit BEGIN TRANSACTION -Anweisung und enden mit dem COMMIT oder ROLLBACK Erklärung. Kann auch mit END enden Erklärung. |
SQL-Server | Explizite Transaktionen beginnen mit BEGIN TRANSACTION -Anweisung und enden mit dem COMMIT oder ROLLBACK Erklärung. |
Oracle | Explizite Transaktionen beginnen mit dem SET TRANSACTION -Anweisung und enden mit dem COMMIT oder ROLLBACK Erklärung. |
In vielen Fällen sind bestimmte Schlüsselwörter optional, wenn explizite Transaktionen verwendet werden. Beispielsweise könnten Sie in SQL Server und SQLite einfach BEGIN
verwenden (anstatt BEGIN TRANSACTION
) und/oder Sie könnten mit COMMIT TRANSACTION
enden (im Gegensatz zu nur COMMIT
).
Es gibt auch verschiedene andere Schlüsselwörter und Optionen, die Sie beim Erstellen einer Transaktion angeben können. Sehen Sie sich also die Dokumentation Ihres DBMS für die vollständige Syntax an.
SQL-Transaktionsbeispiel
Hier ist ein Beispiel für eine einfache Transaktion in SQL Server:
BEGIN TRANSACTION
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;
In diesem Fall werden Bestellinformationen aus zwei Tabellen gelöscht. Beide Anweisungen werden als eine Arbeitseinheit behandelt.
Wir könnten bedingte Logik in unsere Transaktion schreiben, um sie im Falle eines Fehlers rückgängig zu machen.
Eine Transaktion benennen
Bei einigen DBMS können Sie Ihren Transaktionen einen Namen geben. In SQL Server können Sie Ihren gewählten Namen nach dem BEGIN
hinzufügen und COMMIT
Aussagen.
BEGIN TRANSACTION MyTransaction
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;
SQL-Transaktions-Rollback-Beispiel 1
Hier ist noch einmal das vorherige Beispiel, aber mit etwas zusätzlichem Code. Der Typenzusatz dient zum Rollback der Transaktion im Fehlerfall.:
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
Der TRY...CATCH
-Anweisung implementiert die Fehlerbehandlung in SQL Server. Sie können eine beliebige Gruppe von T-SQL-Anweisungen in einen TRY
einschließen Block. Dann, wenn ein Fehler im TRY
auftritt -Block wird die Kontrolle an eine andere Gruppe von Anweisungen übergeben, die in einem CATCH
eingeschlossen ist blockieren.
In diesem Fall verwenden wir den CATCH
Block, um die Transaktion rückgängig zu machen. Vorausgesetzt, es befindet sich im CATCH
blockiert, erfolgt ein Rollback nur, wenn ein Fehler vorliegt.
SQL-Transaktions-Rollback-Beispiel 2
Schauen wir uns die Datenbank genauer an, aus der wir gerade Zeilen gelöscht haben.
Im vorherigen Beispiel haben wir Zeilen aus Orders
gelöscht und OrderItems
Tabellen in der folgenden Datenbank:
In dieser Datenbank wird jedes Mal, wenn ein Kunde eine Bestellung aufgibt, eine Zeile in die Orders
eingefügt Tabelle und eine oder mehrere Zeilen in die OrderItems
Tisch. Die Anzahl der in OrderItems
eingefügten Zeilen hängt davon ab, wie viele verschiedene Produkte der Kunde bestellt.
Wenn es sich um einen neuen Kunden handelt, wird außerdem eine neue Zeile in Customers
eingefügt Tabelle.
In diesem Fall müssen Zeilen in drei Tabellen eingefügt werden.
Im Falle eines Fehlers möchten wir nicht, dass eine Zeile in die Orders
eingefügt wird Tabelle, aber keine entsprechenden Zeilen in OrderItems
Tisch. Das würde zu einer Bestellung ohne Bestellpositionen führen. Grundsätzlich wollen wir, dass beide Tabellen komplett aktualisiert werden oder gar nichts.
Es war das gleiche, als wir die Zeilen gelöscht haben. Wir wollten alle Zeilen löschen oder gar keine.
In SQL Server könnten wir die folgende Transaktion für INSERT
schreiben Aussagen.
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
Dieses Beispiel geht davon aus, dass es an anderer Stelle eine Logik gibt, die bestimmt, ob der Kunde bereits in der Datenbank vorhanden ist oder nicht.
Der Kunde könnte außerhalb dieser Transaktion eingefügt worden sein:
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
Wenn die Transaktion fehlschlägt, befindet sich der Kunde immer noch in der Datenbank (jedoch ohne Bestellungen). Die Anwendung müsste prüfen, ob der Kunde bereits existiert, bevor die Transaktion durchgeführt wird.
SQL-Transaktion mit Sicherungspunkten
Ein Sicherungspunkt definiert einen Ort, an den eine Transaktion zurückkehren kann, wenn ein Teil der Transaktion bedingt abgebrochen wird. In SQL Server geben wir einen Sicherungspunkt mit SAVE TRANSACTION savepoint_name
(wobei Speicherpunktname ist der Name, den wir dem Sicherungspunkt geben).
Lassen Sie uns das vorherige Beispiel umschreiben, um einen Sicherungspunkt einzuschließen:
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;
Hier haben wir direkt nach dem Kunden INSERT
einen Sicherungspunkt gesetzt Erklärung. Später in der Transaktion verwende ich den ROLLBACK
-Anweisung, um die Transaktion anzuweisen, zu diesem Sicherungspunkt zurückzukehren.
Wenn ich diese Anweisung ausführe, wird der Kunde eingefügt, aber keine Bestellinformationen werden eingefügt.
Wenn eine Transaktion auf einen Sicherungspunkt zurückgesetzt wird, muss sie bei Bedarf mit weiteren SQL-Anweisungen und einem COMMIT TRANSACTION
abgeschlossen werden Anweisung, oder sie muss vollständig abgebrochen werden, indem die gesamte Transaktion rückgängig gemacht wird.
Wenn ich den ROLLBACK
bewege Anweisung zurück zum vorherigen INSERT
Anweisung, etwa so:
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;
Dies erzeugt einen Fremdschlüsselkonfliktfehler. Insbesondere erhalte ich die folgende Fehlermeldung:
(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)
Dies geschah, weil, obwohl die Bestellung bereits eingefügt worden war, dieser Vorgang rückgängig gemacht wurde, als wir zum Sicherungspunkt zurückkehrten. Dann wurde die Transaktion abgeschlossen. Aber als es auf den letzten Bestellartikel stieß, gab es keine entsprechende Bestellung (weil diese rückgängig gemacht wurde), und wir bekamen den Fehler.
Als ich die Datenbank überprüfe, wurde der Kunde eingefügt, aber auch hier wurden keine Bestellinformationen eingefügt.
Sie können bei Bedarf an mehreren Stellen in der Transaktion auf denselben Sicherungspunkt verweisen.
In der Praxis würden Sie die bedingte Programmierung verwenden, um die Transaktion an einen Savepont zurückzugeben.
Verschachtelte Transaktionen
Sie können Transaktionen bei Bedarf auch in anderen Transaktionen verschachteln.
So:
BEGIN TRANSACTION Transaction1;
UPDATE table1 ...;
BEGIN TRANSACTION Transaction2;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRANSACTION Transaction2;
UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;
Wie bereits erwähnt, hängt die genaue Syntax, die Sie zum Erstellen einer Transaktion verwenden, von Ihrem DBMS ab. Überprüfen Sie daher die Dokumentation Ihres DBMS, um sich einen vollständigen Überblick über Ihre Optionen beim Erstellen von Transaktionen in SQL zu verschaffen.