【実務・中級編】 トランザクション制御(BEGIN, COMMIT, ROLLBACK) – PostgreSQL

PostgreSQLのトランザクション制御:BEGIN, COMMIT, ROLLBACKを使いこなそう!

やあ、みんな!この記事では、PostgreSQLにおけるトランザクション制御の基本、つまり `BEGIN`、`COMMIT`、`ROLLBACK` について、現場で実際に役立つように、ちょっとしたコツなんかも交えながら解説していくよ。

データベースを触っていると、必ずぶつかるのが「トランザクション」って概念。これ、言葉だけ聞くと難しそうに聞こえるかもしれないけど、実は「一連の処理を、全部成功させるか、全部失敗させるか」っていう、とっても分かりやすい考え方なんだ。

なんでトランザクションが必要なの?~ACID特性のお話~

まず、なんでトランザクションが大事なのか、その理由から触れておこう。データベースの世界では、「ACID特性」っていうのが、データの信頼性を守るための基本中の基本なんだ。

  • Atomicity(原子性): トランザクションは「 all or nothing 」、つまり、一連の処理はすべて成功するか、そうでなければ何もなかったことになる。途中で失敗したら、それまでの処理は全部元に戻るんだ。
  • Consistency(一貫性): トランザクションの前後で、データベースの状態は常に一貫している必要がある。例えば、お金の移動で、片方から減ってもう片方に増えなかったら、データが壊れちゃうよね。
  • Isolation(独立性): 複数のトランザクションが同時に動いても、互いに干渉しないように、それぞれのトランザクションは独立して実行される。
  • Durability(永続性): 一度コミットされた(確定された)トランザクションは、システム障害が発生しても失われることはない。

このACID特性、特に原子性(Atomicity)を実現するために、トランザクション制御が不可欠なんだ。

現場で使うトランザクション制御の基本:BEGIN, COMMIT, ROLLBACK

じゃあ、具体的にどうやってトランザクションを制御するのか見ていこう。PostgreSQLでは、以下の3つのコマンドが基本になるよ。

1. トランザクションの開始:BEGIN

新しいトランザクションを開始したいときは、`BEGIN` コマンドを使う。

BEGIN;

これだけ。簡単でしょ? `BEGIN` を実行したら、その後に続くSQL文は、すべて一つのトランザクションとして扱われるようになるんだ。

2. トランザクションの確定:COMMIT

一連の処理がすべてうまくいった!よし、これで確定させよう!ってときに使うのが `COMMIT` コマンド。

COMMIT;

`COMMIT` を実行すると、`BEGIN` から `COMMIT` までの間に実行されたSQL文の結果が、データベースに正式に記録される。もう元には戻せない、という状態になるわけだ。

3. トランザクションの取り消し:ROLLBACK

あれ?なんか処理でエラーが出ちゃった!とか、やっぱりこの処理はキャンセルしたい!ってときに使うのが `ROLLBACK` コマンド。

ROLLBACK;

`ROLLBACK` を実行すると、`BEGIN` から `ROLLBACK` までの間に実行されたSQL文はすべて無効になり、トランザクション開始前の状態に戻る。まるで何もなかったかのようにね。

具体的な使用例を見てみよう!

例えば、ユーザーの口座から別口座へ送金する処理を考えてみよう。これは、口座Aからお金を減らす処理と、口座Bにお金を増やす処理の、両方が成功しないとおかしいよね。片方だけ成功して、もう片方が失敗したら、お金が消えちゃう!

こんなとき、トランザクションを使うと安全に処理できるんだ。

— 1. トランザクションを開始
BEGIN;

— 2. 口座Aから1000円減らす
UPDATE accounts SET balance = balance – 1000 WHERE account_id = 101;

— 3. 口座Bに1000円増やす
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 202;

— 4. もし、ここまでの処理でエラーがなければ、コミットして確定
COMMIT;

もし、口座Aの更新は成功したけど、口座Bの更新で何らかのエラー(例えば、口座Bが存在しないとか)が発生した場合は、PostgreSQLは自動的にロールバック(または、エラーが発生したSQL文だけ失敗して、それ以降の処理は実行されない)してくれる。

逆に、もし開発中に「ちょっとこの処理、大丈夫かな?」って確認したいときのために、意図的にエラーを発生させてロールバックさせるなんてこともできる。

— テスト用のトランザクション
BEGIN;

— 何か処理を実行…
INSERT INTO some_table (id, name) VALUES (1, ‘test’);

— 意図的にエラーを発生させて、トランザクション全体をロールバックさせる
— 例えば、NOT NULL制約があるカラムにNULLを入れるなど
INSERT INTO another_table (id, value) VALUES (2, NULL);

— もし上のINSERTでエラーが出たら、それまでのINSERTも無効になる
ROLLBACK;

こんな風に、トランザクションはデータの整合性を保つために、ものすごく重要な役割を果たしているんだ。

もっと便利に!セーブポイントを使おう

さて、トランザクションの基本は分かったと思うけど、もっと細かい制御がしたいときもあるよね?例えば、「ここまでは確定させたいけど、その後の処理で問題があったら、ここまでの部分だけ戻したい」なんてケース。

そんなときに役立つのが セーブポイント だ。

セーブポイントは、トランザクションの途中に「印」をつけておくようなイメージ。そして、その印まで戻りたいときに使うコマンドなんだ。

セーブポイントの設定:SAVEPOINT

セーブポイントを設定するには `SAVEPOINT` コマンドを使う。

SAVEPOINT my_savepoint;

「`my_savepoint`」の部分は、好きな名前をつけられるよ。

セーブポイントへのロールバック:ROLLBACK TO SAVEPOINT

設定したセーブポイントに戻りたいときは、`ROLLBACK TO SAVEPOINT` コマンドを使う。

ROLLBACK TO SAVEPOINT my_savepoint;

これを実行すると、`my_savepoint` を設定した以降の処理はすべて取り消されるけど、それ以前の処理はそのまま残るんだ。

セーブポイントの解除(不要になった場合)

セーブポイントは、コミットされると自動的に消えるんだけど、明示的に不要になった場合は `RELEASE SAVEPOINT` で解除することもできるよ。

RELEASE SAVEPOINT my_savepoint;

セーブポイントの活用例

例えば、ちょっと複雑な処理で、いくつかのステップがあるとする。

BEGIN;

— ステップ1:データAを更新
UPDATE data_a SET value = ‘new_value_a’ WHERE id = 1;
SAVEPOINT step1_done; — ステップ1完了の印

— ステップ2:データBを更新
UPDATE data_b SET value = ‘new_value_b’ WHERE id = 2;
SAVEPOINT step2_done; — ステップ2完了の印

— ステップ3:データCを更新
UPDATE data_c SET value = ‘new_value_c’ WHERE id = 3;

— ここで、もしステップ3で問題が発生したら、ステップ2までは残したい場合
— SELECT some_function_that_might_fail();
— IF error_occurred THEN
— ROLLBACK TO SAVEPOINT step2_done;
— END IF;

— もし、すべてうまくいったらコミット
COMMIT;

このように、セーブポイントを使うと、トランザクションの途中で柔軟に状態を管理できるようになるんだ。

まとめ

今日はPostgreSQLのトランザクション制御、`BEGIN`、`COMMIT`、`ROLLBACK`、そしてセーブポイントについて解説したよ。

  • `BEGIN`: トランザクション開始
  • `COMMIT`: トランザクション確定
  • `ROLLBACK`: トランザクション取り消し
  • `SAVEPOINT`: トランザクション途中の印
  • `ROLLBACK TO SAVEPOINT`: 印まで戻る

これらのコマンドをしっかり理解して使いこなすことで、データの整合性を保ち、安全で信頼性の高いアプリケーション開発に繋がるはずだよ。

最初は少し戸惑うかもしれないけど、実際に手を動かして色々なケースで試してみるのが一番の近道!ぜひ、今日からトランザクション制御を意識して、データベース操作に磨きをかけていこう!

もし分からないことがあったら、いつでも聞いてね!

コメント

タイトルとURLをコピーしました