SQL のトランザクションとは、簡単に言ってしまえば、複数の SQL 文によるデータ更新を1つの処理としてまとめてデータベースに反映させることです。
例えば、預金の振替処理を考えると、
(1) 口座Aから1万円を引き落とす(残高が減る)、
(2) 口座Bに1万円を振り込む(残高が増える)、
という処理があり、2つの UPDATE 文を実行する必要があります。
あるいは売上伝票の入力を考えると、
(3) 伝票ヘッダ(顧客情報、売上日時など)の入力
(4) 明細行(商品名、単価、個数など)の入力
というように、複数のテーブルに INSERT 文を実行する必要があります。
いずれの場合も、その一部だけがデータベースに反映されると、一時的にせよ、データベース全体としてデータが不整合な状態になりますが、トランザクションの機能を使えば、これらの処理を同時に実行することができます。
PostgreSQL では、BEGIN 文または START TRANSACTION 文でトランザクションが開始されます。この後 COMMIT 文を実行するまでは、INSERT/UPDATE/DELETE によるデータ更新は保留され、データベースにはすぐには反映されません。COMMIT 文を実行することで、トランザクションが終了し、データの変更がまとめてデータベースに反映されます。また ROLLBACK 文を実行することでもトランザクションが終了しますが、この場合はトランザクション内でのデータの変更がすべてキャンセルされます。
トランザクションの機能を使わずに実行される、つまり BEGIN ~ COMMIT の外側で実行される SQL 文は、即座にその変更がデータベースに反映されます。
RDBMS の種類によっては、BEGIN あるいは START TRANSACTION を実行しなくても自動的にトランザクションが開始され、COMMIT を実行しなければデータベースに反映されない、というものもありますので、利用している RDBMS の仕様がどうなっているか注意してください。
さて、トランザクション機能自体はほとんどの RDBMS がサポートしていますが、その動作における微妙な違いがいくつかありますので、それらについて紹介します。
もっとも重要な違いは、トランザクションの途中でエラーが発生した場合の処理です。
PostgreSQL では、トランザクション中でエラーが発生すると、そのトランザクション自体がエラーにされてしまうため、以後、すべての SQL がエラーになります。SQL の文法エラーだけでなく、一意性などテーブルの制約違反もエラーとして扱われます。この状態で正常に受け付けられるのは COMMIT と ROLLBACK だけです。ただし、COMMIT を実行すると、内部的に ROLLBACK が実行され、トランザクション内で実行したすべての変更が失われてしまいますので、事実上、ROLLBACK 以外は実行できません。
この問題を回避するためには、エラーが発生する可能性のある SQL 文を実行する前に SAVEPOINT を実行する、という方法があります。SQL でエラーが発生したときは、その SAVEPOINT まで ROLLBACK することで、以前の更新を失わずに処理を続行できます。
なお、例えば Oracle では、トランザクションの途中のエラーは単に無視されるだけで、トランザクション全体には影響を及ぼさないようです。
これに比べると重要度としては低いですが、DDL を実行した時の動作の違いもあります。
PostgreSQL では、CREATE TABLE や DROP TABLE などの DDL もトランザクションの一部となるため、トランザクションの途中で DROP TABLE を実行した場合でも、最後に ROLLBACK すれば、DROP したテーブルが元に戻ります。
例えば Oracle では、DDL を実行すると、その時点で「自動 COMMIT」が発生し、それ以前の更新が自動的にデータベースに反映されます。DDL はトランザクションの一部にはならないので、ROLLBACK できません。
データベースアプリケーションの開発において、トランザクションの一部として DDL が使える、ということは機能的に重要というわけではありませんから、PostgreSQL を前提に開発する場合でも、DDL はトランザクションの外側に置くようにすべきでしょう。一方で、自動 COMMIT を前提にするのも正しいやり方ではありませんから、DDL による自動 COMMIT のある RDBMS を使う場合でも、DDL の実行前に明示的に COMMIT を実行するようにすべきでしょう。
もう一つ、やはり重要度としては低いですが、注意が必要な点があります。
psql でデータベースにアクセスして、BEGIN でトランザクションを開始した後、COMMIT も ROLLBACK も実行せずに \q メタコマンド(あるいは CTRL-D)でpsql ツールを終了したらどうなるでしょうか。この場合、実行中のトランザクションは ROLLBACK され、変更はキャンセルされます。
Oracle では、sqlplus というツールでデータベースにアクセスしますが、こちらは、COMMIT せずにツールを終了した場合、それまでの変更分がデータベースに反映されます、つまり自動的に COMMIT が実行されてしまいます(DDL によるものとは違って「自動 COMMIT」とは呼ばれないようです)。
どちらを使うにせよ、明示的に COMMIT あるいは ROLLBACK を実行してトランザクションを終了させてからクライアントツールを終了するのが望ましいことは言うまでもありませんが、終了時の動作がツールによって異なることには注意しておきましょう。
解説:松田神一
© EDUCO All Rights Reserved.