チュートリアル: テーブル間でトランザクションを調整する

Important

Unity カタログのマネージド デルタ テーブルに書き込むトランザクションは 、パブリック プレビュー段階にあります

Unity カタログで管理されている Iceberg テーブルに書き込むトランザクションは 、プライベート プレビュー段階にあります。 このプレビューに参加するには、 管理された Iceberg テーブルのプレビュー登録フォームを送信します

このチュートリアルでは、トランザクション モードの両方を使用して、Azure Databricks上の複数のステートメントとテーブルの更新を調整します。非対話型 (BEGIN ATOMIC)、自動的にコミットされます。対話型 (BEGIN TRANSACTION) を使用すると、明示的に制御できます。 このチュートリアルでは、ストアド プロシージャと SQL スクリプトでトランザクションを使用する方法についても説明します。

必要条件

  • Environment: Azure Databricks ワークスペースへのアクセス。
  • コンピューティング: サポートされているコンピューティングの種類は、トランザクション モードによって異なります。
    • クラシック SQL ウェアハウスまたはサーバーレス SQL ウェアハウス では、両方のトランザクション モードがサポートされています。
    • サーバーレス コンピューティング では、非対話型トランザクションのみがサポートされます。
    • Databricks Runtime 18.0 以降を実行しているクラシック クラスター では、非対話型トランザクションのみがサポートされます。
  • 特権: CREATE TABLE スキーマでの

サンプル テーブルを設定する

複数ステートメントで書き込まれるすべてのテーブルでは、複数テーブル トランザクションで次の処理を行う必要があります。

SQL エディターまたはノートブックで 2 つのサンプル テーブルを作成します。

-- Account data
CREATE TABLE IF NOT EXISTS sample_accounts (
  id INT,
  account_name STRING,
  balance DECIMAL(10,2)
) USING DELTA
TBLPROPERTIES (
  'delta.feature.catalogManaged' = 'supported'
);

-- Transaction records
CREATE TABLE IF NOT EXISTS sample_transactions (
  id INT,
  account_id INT,
  transaction_type STRING,
  amount DECIMAL(10,2)
) USING DELTA
TBLPROPERTIES (
  'delta.feature.catalogManaged' = 'supported'
);

Note

既存のテーブルでトランザクションを有効にするには、次のコマンドを実行します。

ALTER TABLE <table_name> SET TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');

両方のテーブルにサンプル データを挿入します。

INSERT INTO sample_accounts VALUES
  (1, 'Alice', 1000.00),
  (2, 'Bob', 500.00);

INSERT INTO sample_transactions VALUES
  (1, 1, 'deposit', 100.00);

セットアップを確認します。

SELECT * FROM sample_accounts;
SELECT * FROM sample_transactions;

アウトプット:

sample_accounts:
id	account_name	balance
1	  Alice	        1000.00
2	  Bob	          500.00

sample_transactions:
id	account_id	transaction_type	amount
1	           1	         deposit	100.00

非対話型トランザクション

非対話型トランザクションでは、 BEGIN ATOMIC ... END; 構文が使用されます。 すべてのステートメントは、1 つのアトミック単位として実行されます。 すべてのステートメントが成功すると、Azure Databricksは自動的にコミットされます。 ステートメントが失敗した場合、Azure Databricksはすべての変更を自動的にロールバックします。 構文と使用パターンの詳細については、 非対話型トランザクションを参照してください。

成功したトランザクションを実行する

両方のテーブルをアトミックに更新します。

BEGIN ATOMIC
  -- Update Alice's account balance
  UPDATE sample_accounts
  SET balance = balance + 100.00
  WHERE id = 1;

  -- Record the deposit transaction
  INSERT INTO sample_transactions
  VALUES (2, 1, 'deposit', 100.00);
END;

Alice の残高が 1100.00 になっていることを確認します。

SELECT * FROM sample_accounts WHERE id = 1;

次の 2 つのトランザクション レコードが存在するかどうかを確認します。

SELECT * FROM sample_transactions;

残高更新とトランザクション レコードの両方が一緒に作成されました。 いずれかのステートメントが失敗した場合、どちらの変更もコミットされず、Databricks は副作用なしにトランザクションを終了します。

SIGNAL を使用して条件に基づいてトランザクションを失敗させる

SIGNAL ブロック内のBEGIN ATOMIC ... END;を使用して、ユーザー定義の条件が満たされていない場合にトランザクションを失敗させることができます。 次の使用例は、負の残高を持つ勘定を挿入し、残高チェックが失敗した場合に SIGNAL を使用してトランザクションを失敗させます。

BEGIN ATOMIC
  INSERT INTO sample_accounts VALUES (3, 'Charlie', -50.00);

  IF (SELECT balance FROM sample_accounts WHERE id = 3) < 0 THEN
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Account balance cannot be negative';
  END IF;
END;

SIGNALによってエラーが発生し、トランザクション全体が自動的にロールバックされます。 挿入がロールバックされたため、0 行が返されます。

SELECT * FROM sample_accounts WHERE id = 3;

失敗時の自動ロールバックを確認する

最初のステートメントが有効であるが、2 番目のステートメントが存在しないテーブルを参照するトランザクションを実行します。

BEGIN ATOMIC
  -- Valid
  INSERT INTO sample_accounts VALUES (4, 'David', 300.00);
  -- Invalid
  INSERT INTO non_existent_table VALUES (1, 2, 3);
END;

トランザクションはエラーで失敗します。 トランザクション全体がロールバックされたため、0 行が返されます。

SELECT * FROM sample_accounts WHERE id = 4;

最初の INSERT ステートメントは有効でしたが、2 つ目のステートメントが失敗したため、ロールバックされました。 これは、トランザクションの全保証または無保証を示しています。

対話型トランザクション

対話型トランザクションを使用すると、コミットまたはロールバックするタイミングを明示的に制御できます。 BEGIN TRANSACTION を使用して開始し、COMMIT を使用して変更を保存するか、ROLLBACK を使用して変更を破棄します。

変更をコミットする

トランザクションを開始します。

BEGIN TRANSACTION;

変更を加えます (まだコミットされていません)。

INSERT INTO sample_accounts VALUES (5, 'Eve', 850.00);
UPDATE sample_accounts SET balance = balance + 50.00 WHERE id = 2;

変更を確定して永続化する

COMMIT;

Eve のアカウントが表示されていることを確認します。

SELECT * FROM sample_accounts WHERE id = 5;

Bobの残高が現在550.00であることを確認します:

SELECT * FROM sample_accounts WHERE id = 2;

変更をロールバックする

新しいトランザクションを開始します。

BEGIN TRANSACTION;

変更を加えます。

INSERT INTO sample_accounts VALUES (6, 'Frank', 600.00);

変更がセッションに表示されていることを確認します (行はコミットされるまで他のセッションには表示されません)。

SELECT * FROM sample_accounts WHERE id = 6;

ロールバックして変更を破棄する:

ROLLBACK;

挿入がロールバックされたため、0 行が返されます。

SELECT * FROM sample_accounts WHERE id = 6;

ストアド プロシージャと SQL スクリプトで使用する

トランザクションと ストアド プロシージャ を組み合わせて、再利用可能なトランザクション ロジックを作成できます。 このパターンは、頻繁に実行する複雑な操作に役立ちます。

  1. カタログ コミットを有効にしてテーブルを作成する

    CREATE SCHEMA IF NOT EXISTS main.retail;
    
    CREATE TABLE IF NOT EXISTS main.retail.orders (
      order_id STRING,
      customer_id STRING,
      amount DECIMAL(18,2)
    ) TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');
    
    CREATE TABLE IF NOT EXISTS main.retail.orders_staging (
      order_id STRING,
      customer_id STRING,
      amount DECIMAL(18,2),
      batch_id STRING
    ) TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');
    
    CREATE TABLE IF NOT EXISTS main.retail.total_sales (
      customer_id STRING,
      total_amount DECIMAL(18,2)
    ) TBLPROPERTIES ('delta.feature.catalogManaged' = 'supported');
    
  2. ストアド プロシージャを定義する

    CREATE OR REPLACE PROCEDURE main.retail.apply_order(
        IN  p_order_id      STRING,
        IN  p_customer_id   STRING,
        IN  p_order_amount  DECIMAL(18,2)
    )
    LANGUAGE SQL
    SQL SECURITY INVOKER
    MODIFIES SQL DATA
    AS
    BEGIN
        -- Insert the order
        INSERT INTO main.retail.orders (order_id, customer_id, amount)
        VALUES (p_order_id, p_customer_id, p_order_amount);
    
        -- Update total sales per customer
        MERGE INTO main.retail.total_sales AS t
        USING (
            SELECT
              p_customer_id  AS customer_id,
              p_order_amount AS order_amount
        ) s
          ON t.customer_id = s.customer_id
        WHEN MATCHED THEN
          UPDATE SET t.total_amount = t.total_amount + s.order_amount
        WHEN NOT MATCHED THEN
          INSERT (customer_id, total_amount)
          VALUES (s.customer_id, s.order_amount);
    END;
    
    
  3. トランザクションを定義する

    BEGIN ATOMIC
        -- Staging batch id for this transaction
        DECLARE new_order_id STRING DEFAULT uuid();
        DECLARE v_batch_id STRING DEFAULT uuid();
    
        -- 1) Stage incoming customer and order rows
        INSERT INTO main.retail.orders_staging (order_id, customer_id, amount, batch_id)
        VALUES (new_order_id, 'CUST_123', 249.99, v_batch_id);
    
        -- 2) Drive final writes from staging to production via stored procedure
        FOR o AS
          SELECT
            order_id,
            customer_id,
            amount
          FROM main.retail.orders_staging
          WHERE batch_id = v_batch_id
        DO
            CALL main.retail.apply_order(
              o.order_id,
              o.customer_id,
              o.amount
            );
        END FOR;
    
        -- 3) Clean up processed staging rows
        DELETE FROM main.retail.orders_staging
        WHERE batch_id = v_batch_id;
    END; -- 4) Commit the transaction
    
    

トランザクションの一部が失敗した場合、Databricks はすべての変更を自動的にロールバックします。

クリーンアップ

サンプル テーブルを削除します。

DROP TABLE IF EXISTS sample_accounts;
DROP TABLE IF EXISTS sample_transactions;

DROP TABLE IF EXISTS main.retail.orders;
DROP TABLE IF EXISTS main.retail.orders_staging;
DROP TABLE IF EXISTS main.retail.total_sales;

その他のリソース