SQLite3でトランザクションを使ってみる

トランザクション(Transaction)とはいくつかの作業をひとまとめにして、全部成功するか、全部やめるかのどちらかにする機能になります。


使用例としまして、花子さんからよしこさんにお金を入金するとします。

データベース内でのお金の流れとしまして、

・花子さんの残金を◯円減らす

・よしこさんの残金を◯円増やす

になります。


この時、花子さんの残金を減らした後によしこさんの残金を増やす間にエラーが発生しますと、データベース全体の残金のうち、◯円が消失したままの状態になります。


上記のようなやりとりの途中でエラーが発生した場合にやり取りを開始する前の状態に戻す必要があり、戻すための仕組みとしてトランザクションを利用します。




トランザクションに関して、クエリの例を用いて見てみます。

パイソンでgimeiでSQLite3のRDB用のテストデータを用意してみるを使いながらトランザクションを使ってみます。


トランザクションをする上で大事になるのがトランザクション制御言語(TCL:Transaction Control Language)になり、よく使うものは下記の三個になります。

BEGIN TRANSACTION:トランザクション開始

COMMIT:データ挿入を確定

ROLLBACK:データ挿入を取り消して、データベースをBEGINより前の状態に戻す


最初にエラーなくデータを挿入できるクエリを実行してみます。

トランザクションを使う前に

sqlite> .bail on

を実行します。

.bailはエラーが起きたら即座に実行を中止するかどうかの設定項目になります。


usersテーブルに新規でユーザーを追加した後、新規ユーザーのIDでledgerにデータを挿入するクエリを実行してみます。

BEGIN TRANSACTION;
INSERT INTO users (name) VALUES ('花子');
INSERT INTO ledger (user_id, amount) VALUES (last_insert_rowid(), 5000);
COMMIT;

をまとめてコピーして、SQLite3のプロンプトに貼り付けて実行してみます。

last_insert_rowid()は一つ前のクエリで実行した後に最後に挿入したデータのIDを返すという処理になり、今回の場合であれば花子さんのIDを返します。


上記のクエリではエラーは発生しないので、

sqlite> SELECT * FROM users ORDER BY id DESC LIMIT 1;
+-----+------+
| id  | name |
+-----+------+
| 109 | 花子   |
+-----+------+

で花子さんが挿入されています。


花子さんのIDを使って、

sqlite> SELECT * FROM ledger WHERE user_id = 109;
+---------+--------+---------------------+
| user_id | amount |     created_at      |
+---------+--------+---------------------+
| 109     | 5000   | 2026-05-10 16:10:53 |
+---------+--------+---------------------+

ledgerに挿入されたデータも確認しておきます。




次にエラーが出るようなクエリを作ってみます。

BEGIN TRANSACTION;
INSERT INTO users (name) VALUES ('よしこ');
INSERT INTO ledger (user_id, amount) VALUES (last_insert_rowid());
COMMIT;

ledgerテーブルの方でデータ数が足りずにエラーになるクエリを用意して、このコードをコピーして、SQLite3のプロンプトに貼り付けて実行してみます。


Parse error: 1 values for 2 columns

のようなエラーになります。


sqlite> SELECT * FROM users ORDER BY id DESC LIMIT 1;

を実行してみると、

+-----+------+
| id  | name |
+-----+------+
| 110 | よしこ  |
+-----+------+

よしこさんのデータは挿入されていますが、

sqlite> SELECT * FROM ledger WHERE user_id = 110;

ではデータが取得できません。


ここで

sqlite> ROLLBACK;

を実行した後、

sqlite> SELECT * FROM users ORDER BY id DESC LIMIT 1;

を実行してみると、

+-----+------+
| id  | name |
+-----+------+
| 109 | 花子   |
+-----+------+

一つ前の花子さんが最後に挿入されたデータになっていて、よしこさんのデータの挿入がなかった事になっています。


データベースがトランザクションを開始する前に戻りました。

Transaction - SQLite

京都の東本願寺で開催されているプログラミング教室で講師をしています。
詳しくはTera schoolを御覧ください。
マインクラフト用ビジュアルエディタを開発しています。

詳しくはinunosinsi/mcws_blockly - githubをご覧ください。