sql >> データベース >  >> RDS >> Database

SQLトランザクションチュートリアル

    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のドキュメントを確認してください。


    1. SQLステートメントを使用してテーブルに列が存在するかどうかをテストするにはどうすればよいですか?

    2. MySQLINクエリで順序を維持する

    3. Oracleで英数字のみを含む行を返す2つの方法

    4. MySQLクローンプラグインとXtrabackupの比較