SQLでは、一連のSQLステートメントが完全に実行されるか、まったく実行されないようにすることで、トランザクションを使用してデータの整合性を維持します。
トランザクションは、単一の作業単位として実行する必要があるSQLステートメントのシーケンスを管理するため、データベースに部分的な操作の結果が含まれることはありません。
トランザクションがデータベースに複数の変更を加える場合、トランザクションがコミットされたときにすべての変更が成功するか、トランザクションがロールバックされたときにすべての変更が取り消されます。
いつトランザクションを使用しますか?
一連のSQLステートメントのいずれかが失敗した場合にデータの整合性が危険にさらされる状況では、トランザクションが最も重要です。
たとえば、ある銀行口座から別の銀行口座にお金を移動する場合、一方の口座からお金を差し引き、もう一方の口座に追加する必要があります。途中で失敗することは望ましくありません。そうしないと、一方のアカウントからお金が引き落とされても、もう一方のアカウントには入金されない可能性があります。
失敗の考えられる理由には、資金不足、アカウント番号の無効、ハードウェア障害などがあります。
だからあなたはしない このような状況になりたい:
Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
それは本当に 悪い。データベースには一貫性のないデータが含まれ、お金は空中に消えてしまいます。そうすると、銀行は顧客を失い(これが続けば、銀行はおそらくすべての顧客を失うでしょう)、あなたは仕事を失うでしょう。
仕事を節約するために、次のようなトランザクションを使用できます。
START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION
そのトランザクション内に、問題が発生した場合にトランザクションをロールバックする条件付きロジックを記述できます。
たとえば、借方勘定1と貸方勘定2の間で問題が発生した場合、トランザクション全体がロールバックされます。
したがって、考えられる結果は2つだけです。
Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
または:
Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)
これは簡略化された図ですが、SQLトランザクションがどのように機能するかを示す古典的な図です。 SQLトランザクションにはACIDがあります。
トランザクションタイプ
SQLトランザクションは、次のモードで実行できます。
トランザクションモード | 説明 |
---|---|
トランザクションの自動コミット | 個々のステートメントはトランザクションです。 |
暗黙のトランザクション | 前のトランザクションが完了すると、新しいトランザクションが暗黙的に開始されますが、各トランザクションは、通常はCOMMIT を使用して明示的に完了します。 またはROLLBACK DBMSに応じたステートメント。 |
明示的なトランザクション | START TRANSACTION などの行で明示的に開始 、BEGIN TRANSACTION または同様のもので、DBMSに応じて、関連するステートメントで明示的にコミットまたはロールバックされます。 |
バッチスコープのトランザクション | 複数のアクティブな結果セット(MARS)にのみ適用されます。 MARSセッションで開始される明示的または暗黙的なトランザクションは、バッチスコープのトランザクションになります。 |
使用可能な正確なモードとオプションは、DBMSによって異なる場合があります。この表は、SQLServerで使用可能なトランザクションモードの概要を示しています。
この記事では、主に明示的なトランザクションに焦点を当てています。
暗黙的なトランザクションと自動コミットの違いについては、SQLServerでの暗黙的なトランザクションのしくみを参照してください。
Sytnax
次の表は、いくつかの一般的なDBMSで明示的なトランザクションを開始および終了するための基本的な構文の概要を示しています。
DBMS | 明示的なトランザクション構文 |
---|---|
MySQL、MariaDB、PostgreSQL | 明示的なトランザクションはSTART TRANSACTION で始まります またはBEGIN 声明。 COMMIT 現在のトランザクションをコミットし、その変更を永続的にします。 ROLLBACK 現在のトランザクションをロールバックし、変更をキャンセルします。 |
SQLite | 明示的なトランザクションはBEGIN TRANSACTION で始まります ステートメントで終わり、COMMIT で終わります またはROLLBACK 声明。 END で終了することもできます ステートメント。 |
SQL Server | 明示的なトランザクションはBEGIN TRANSACTION で始まります ステートメントで終わり、COMMIT で終わります またはROLLBACK ステートメント。 |
Oracle | 明示的なトランザクションはSET TRANSACTION で始まります ステートメントで終わり、COMMIT で終わります またはROLLBACK ステートメント。 |
多くの場合、明示的なトランザクションを使用する場合、特定のキーワードはオプションです。たとえば、SQL ServerとSQLiteでは、BEGIN
を使用するだけです。 (BEGIN TRANSACTION
ではなく )および/またはCOMMIT TRANSACTION
で終了することもできます (COMMIT
だけではありません 。
トランザクションを作成するときに指定できる他のさまざまなキーワードやオプションもあるため、完全な構文については、DBMSのドキュメントを参照してください。
SQLトランザクションの例
SQLServerでの単純なトランザクションの例を次に示します。
BEGIN TRANSACTION
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;
この場合、注文情報は2つのテーブルから削除されます。どちらのステートメントも1つの作業単位として扱われます。
条件付きロジックをトランザクションに書き込んで、エラーが発生した場合にロールバックさせることができます。
トランザクションの命名
一部のDBMSでは、トランザクションに名前を付けることができます。 SQL Serverでは、BEGIN
の後に選択した名前を追加できます およびCOMMIT
ステートメント。
BEGIN TRANSACTION MyTransaction
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;
SQLトランザクションのロールバックの例1
これも前の例ですが、追加のコードがいくつかあります。追加のコードは、エラーが発生した場合にトランザクションをロールバックするために使用されます。:
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
ステートメントは、SQLServerでのエラー処理を実装します。 T-SQLステートメントの任意のグループをTRY
で囲むことができます ブロック。次に、TRY
でエラーが発生した場合 ブロック、制御はCATCH
で囲まれたステートメントの別のグループに渡されます ブロック。
この場合、CATCH
を使用します トランザクションをロールバックするためにブロックします。 CATCH
にあるとすると ブロック、ロールバックはエラーが発生した場合にのみ発生します。
SQLトランザクションのロールバックの例2
行を削除したばかりのデータベースを詳しく見てみましょう。
前の例では、Orders
から行を削除しました およびOrderItems
次のデータベースのテーブル:
このデータベースでは、顧客が注文するたびに、Orders
に行が挿入されます。 テーブル、およびOrderItems
への1つ以上の行 テーブル。 OrderItems
に挿入された行数 顧客が注文するさまざまな製品の数によって異なります。
また、新規顧客の場合は、Customers
に新しい行が挿入されます テーブル。
その場合、行を3つのテーブルに挿入する必要があります。
失敗した場合、Orders
に行を挿入したくありません。 テーブルですが、OrderItems
に対応する行がありません テーブル。その結果、注文アイテムのない注文になります。基本的に、両方のテーブルを完全に更新するか、まったく更新しないようにします。
行を削除したときも同じでした。すべての行を削除するか、まったく削除しないようにしました。
SQL Serverでは、INSERT
に対して次のトランザクションを記述できます。 ステートメント。
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
この例では、顧客がデータベースにすでに存在するかどうかを決定するロジックが他の場所にあることを前提としています。
顧客はこのトランザクションの外に挿入された可能性があります:
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
トランザクションが失敗した場合でも、顧客はデータベースに残ります(ただし注文はありません)。アプリケーションは、トランザクションを実行する前に、顧客がすでに存在するかどうかを確認する必要があります。
セーブポイントを使用したSQLトランザクション
セーブポイントは、トランザクションの一部が条件付きでキャンセルされた場合にトランザクションが戻ることができる場所を定義します。 SQL Serverでは、SAVE TRANSACTION savepoint_name
( savepoint_name セーブポイントに付ける名前です)。
前の例を書き直して、セーブポイントを含めましょう:
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;
ここでは、顧客のINSERT
の直後にセーブポイントを設定しました 声明。トランザクションの後半で、ROLLBACK
を使用します そのセーブポイントにロールバックするようにトランザクションに指示するステートメント。
そのステートメントを実行すると、顧客は挿入されますが、注文情報は挿入されません。
トランザクションがセーブポイントにロールバックされる場合、必要に応じて追加のSQLステートメントとCOMMIT TRANSACTION
を使用して、トランザクションを完了まで進める必要があります。 ステートメント、またはトランザクション全体をロールバックして完全にキャンセルする必要があります。
ROLLBACK
を移動した場合 前のINSERT
に戻るステートメント このようなステートメント:
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;
これにより、外部キー競合エラーが発生します。具体的には、次のエラーが発生します:
(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)
これは、注文がすでに挿入されていても、セーブポイントにロールバックしたときにその操作が取り消されたために発生しました。その後、トランザクションは完了まで進みました。しかし、最終注文アイテムに遭遇したとき、対応する注文がなく(それが取り消されていたため)、エラーが発生しました。
データベースを確認したところ、顧客は挿入されましたが、注文情報は挿入されませんでした。
必要に応じて、トランザクション内の複数の場所から同じセーブポイントを参照できます。
実際には、条件付きプログラミングを使用して、トランザクションをsavepontに返します。
ネストされたトランザクション
必要に応じて、トランザクションを他のトランザクション内にネストすることもできます。
このように:
BEGIN TRANSACTION Transaction1;
UPDATE table1 ...;
BEGIN TRANSACTION Transaction2;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRANSACTION Transaction2;
UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;
前述のように、トランザクションの作成に使用する正確な構文はDBMSによって異なるため、SQLでトランザクションを作成する際のオプションの全体像については、DBMSのドキュメントを確認してください。