SQL(5) トランザクション
テーブルのレストア
前回までに作成したテーブルを .restore
でレストアしましょう.
また,.table
で【学生リスト】,【学部リスト】,【テニス部】,【サッカー部】,【商品リスト】,【八百屋】,【果物屋】があることを確認しましょう.
sqlite> .restore db4.sqlite
sqlite> .table
サッカー部 八百屋 学生リスト 果物屋
テニス部 商品リスト 学部リスト
学籍番号 | 氏名 | 学年 | 学部 | よみがな |
---|---|---|---|---|
A001 | 岩城隼人 | 2 | 工学部 | いわきはやと |
A002 | 岩村優 | 1 | 工学部 | いわむらゆう |
B003 | 杉江弘子 | 3 | 人文学部 | すぎえひろこ |
C004 | 仙波あすか | 1 | 国際関係学部 | せんばあすか |
D006 | 山下隆 | 4 | 経営情報学部 | やましたたかし |
【学生リスト】
学部 | キャンパス | 教員数 |
---|---|---|
工学部 | 春日井 | 30 |
人文学部 | 春日井 | 20 |
国際関係学部 | 名古屋 | 25 |
【学部リスト】
学籍番号 | 氏名 | 学年 | 学部 | よみがな |
---|---|---|---|---|
A002 | 岩村優 | 1 | 工学部 | いわむらゆう |
B003 | 杉江弘子 | 3 | 人文学部 | すぎえひろこ |
C004 | 仙波あすか | 1 | 国際関係学部 | せんばあすか |
【テニス部】
学籍番号 | 氏名 | 学年 | 学部 | よみがな |
---|---|---|---|---|
A001 | 岩城隼人 | 2 | 工学部 | いわきはやと |
B003 | 杉江弘子 | 3 | 人文学部 | すぎえひろこ |
A005 | 内田弘 | 3 | 工学部 | うちだひろし |
【サッカー部】
商品ID | 商品名 | 仕入れ値 | 売値 |
---|---|---|---|
C01 | テレビ | 50000 | 65000 |
C02 | エアコン | 60000 | 70000 |
C03 | 冷蔵庫 | 80000 | 55000 |
C04 | 電卓 | 3000 | 2000 |
C05 | 炊飯器 | 20000 | 25000 |
【商品リスト】
商品番号 | 商品名 | 価格 |
---|---|---|
G1 | ニンジン | 100 |
G2 | トマト | 200 |
G3 | スイカ | 500 |
【八百屋】
商品番号 | 商品名 | 価格 |
---|---|---|
G4 | イチゴ | 300 |
G5 | ブドウ | 200 |
G3 | スイカ | 500 |
【果物屋】
トランザクション
先ずは,トランザクションについて見ていきましょう. トランザクションとは,データベースに対する分割不可能な一連の処理のことを意味しています. 例えば,学籍番号が$A002$の学生が$人文学部$に転学部したとします. このとき,下記の二つの処理が必要です.
- 【学生リスト】から学籍番号が$A002$のレコードを検索し,学部を$人文学部$に変更
- 【テニス部】から学籍番号が$A002$のレコードを検索し,学部を$人文学部$に変更
もしこの処理が分割されて実行された場合, 【学生リスト】では$人文学部$であるのに,【テニス部】では$工学部$のままとなり矛盾が生じてしまいます. このように矛盾が生じないように,データベースに対する複数の処理を, トランザクションとしてまとめて実行することができます.
また,トランザクションが満たすべき性質として ACID と呼ばれる概念が存在します.
ACID
- 原子性(Atomicity) トランザクションに含まれる処理は,全て実行されるか,全く実行されないことが保証される.
- 独立性(Isolation) トランザクションの実行過程は隠蔽される(他者から観測できない).
- 一貫性(Consistency) トランザクションの終了時に矛盾が発生する処理は実行されない(中断される).
- 永続性(Durability) トランザクションが完了したら,その結果は失われない.
原子性
まずは,原子性に関して確認していきましょう.
トランザクションをスタートするには,BEGIN TRANSACTION
と入力します.
/* トランザクションのスタート */
BEGIN TRANSACTION;
トランザクションをスタートすると,データベースがロックされ,他者は書き込みが出来なくなります (正確にはデータベースへの最初のアクセスがあった時点でロックされる). それでは,学籍番号が$A002$の学生が,$人文学部$に転学部したことを想定し, 【学生リスト】と【テニス部】のレコードをUPDATE文で更新してみましょう.
/* 学生リストの更新 */
UPDATE 学生リスト SET 学部="人文学部" WHERE 学籍番号="A002";
/* テニス部の更新 */
UPDATE テニス部 SET 学部="人文学部" WHERE 学籍番号="A002";
sqlite> select * from 学生リスト;
学籍番号|氏名|学年|学部|よみがな
A001|岩城隼人|2|工学部|いわきはやと
A002|岩村優|1|人文学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
D006|山下隆|4|経営情報学部|やましたたかし
sqlite> select * from テニス部;
学籍番号|氏名|学年|学部|よみがな
A002|岩村優|1|人文学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
【学生リスト】と【テニス部】を確認すると,
確かに学籍番号が$A002$の所属が$人文学部$に変更されていることが確認できます.
しかし,実はこの時点では,更新は確定されておらず,更新前の状態に戻すことが可能です.
前の状態に戻すことを ロールバック と言います.
ロールバックするには,ROLLBACK TRANSACTION
と入力します.
/* トランザクションのロールバック */
ROLLBACK TRANSACTION;
ロールバックすると,【学部リスト】と【テニス部】は更新前の状態に戻っていることが確認できます.
sqlite> select * from 学生リスト;
学籍番号|氏名|学年|学部|よみがな
A001|岩城隼人|2|工学部|いわきはやと
A002|岩村優|1|工学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
D006|山下隆|4|経営情報学部|やましたたかし
sqlite> select * from テニス部;
学籍番号|氏名|学年|学部|よみがな
A002|岩村優|1|工学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
それでは,もう一度,トランザクションをスタートして, 先程と同じように【学部リスト】と【テニス部】を更新しましょう.
BEGIN TRANSACTION;
UPDATE 学生リスト SET 学部="人文学部" WHERE 学籍番号="A002";
UPDATE テニス部 SET 学部="人文学部" WHERE 学籍番号="A002";
今度は,更新を確定しましょう.
確定するには,COMMIT TRANSACTION
と入力します.
これで,更新は確定され,データベースに対するロックも解除されます.
COMMIT TRANSACTION;
このように,トランザクションの処理は全て実行されるか,または, ロールバックで全て取り消されるか,どちらかの結果となります. ここで,一旦,作成したテーブルをファイルにバックアップしましょう. バックアップするファイル名は db5.sqlite としましょう.
sqlite> .backup db5.sqlite
独立性
次に,独立性に関して確認していきましょう.
独立性を確認するためには,複数のクライアントから,データベースに接続する必要があります.
そこで,2つのクライアント(便宜的に クライアントA と クライアントB と呼ぶ)を起動し,
それぞれから.open
を利用して,先程バックアップしたファイル( db5.sqlite )に接続します.
ファイルへの接続は.database
で確認することができます.
これで,クライアントAとクライアントBは同じデータベースを共有していることになります.
/* クライアントAとクライアントBの両方で実行 */
sqlite> .open db5.sqlite
sqlite> .database
main: db5.sqlite
それでは,クライアントAで,トランザクションをスタートし, 学籍番号が$A002$の学部を$工学部$に戻してみましょう.
/* クライアントAで実行 */
BEGIN TRANSACTION;
UPDATE 学生リスト SET 学部="工学部" WHERE 学籍番号="A002";
UPDATE テニス部 SET 学部="工学部" WHERE 学籍番号="A002";
クライアントAで更新を確定していない状態で, クライアントBから【学生リスト】を確認してみます. 学部は更新されておらず$人文学部$のままであることがわかります.
/* クライアントBの【学生リスト】 */
sqlite> select * from 学生リスト;
A001|岩城隼人|2|工学部|いわきはやと
A002|岩村優|1|人文学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
D006|山下隆|4|経営情報学部|やましたたかし
そこで,クライアントAで更新を確定します.
/* クライアントAで実行 */
COMMIT TRANSACTION;
再度,クライアントBから【学生リスト】を確認すると, 学部が$工学部$に更新されていることがわかります. このように,トランザクションの途中経過は, 他のクライアントには隠蔽され,確定した段階で観測できるようになります.
/* クライアントBの【学生リスト】 */
sqlite> select * from 学生リスト;
A001|岩城隼人|2|工学部|いわきはやと
A002|岩村優|1|工学部|いわむらゆう
B003|杉江弘子|3|人文学部|すぎえひろこ
C004|仙波あすか|1|国際関係学部|せんばあすか
D006|山下隆|4|経営情報学部|やましたたかし
課題1
トランザクションを利用して【八百屋】と【果物屋】のスイカの価格を700円に変更しなさい. このとき,2つのクライアントを起動し,一方には途中経過が隠蔽されていることを確認しなさい. また,各クライアントの出力結果(確定前と確定後)を提出しなさい.
テーブルのバックアップ
作成したテーブルをファイルにバックアップするには,.backup
を用います.
バックアップするファイル名は db5.sqlite としましょう.
sqlite> .backup db5.sqlite
情報処理技術者試験・過去問
下記リンクは ITパスポート試験ドットコム, 基本情報技術者試験ドットコム, 応用情報技術者試験ドットコムに掲載されている問題です.