トランザクション処理
会員テーブルの名寄せを行った後、関連テーブルの更新処理でエラーが発生した場合、RDB内で矛盾が生じてしまいます。これを避けるため、一連の処理が途中で失敗した場合は元の状態に戻す「トランザクション処理」を利用します。
トランザクション処理は難しくありません。一連の処理を行う前に「トランザクション開始」命令を発行し、途中でエラーが発生した場合は「ロールバック」命令を発行すれば元の状態に戻ります。すべての更新処理が成功した場合は「コミット」命令を発行して、一連の更新処理を確定します。
第2回の処理では、テーブルは1つ(会員テーブルのみ)でしたが、SQL命令は複数回発行していたので、厳密にはトランザクション処理を行う必要がありましたが、省略していました。今回、関連テーブルの更新に伴い、まとめて行うようにします。
命令 | 内容 | 備考 |
BEGIN | トランザクション開始 | 一連の処理の始まりを指定する(注1) |
COMMIT | 一連の処理を確定する | エラーが発生しなかった場合 |
ROLLBACK | 一連の処理をキャンセルする | トランザクション開始時点の状態に戻す |
上記は、RDBを直接、コマンドインターフェースなどでSQL命令を入力する場合です。プログラムからトランザクション処理を利用する場合は、RDBにアクセスするライブラリの機能を利用して行います。トランザクション開始、コミット、ロールバックの実現方法は、お使いのライブラリの仕様に従ってください。
なお、トランザクション処理の利用にあたっては、各RDBでの条件などがありますので(例。MySQLではInnoDB型のテーブルであることが必須)、お使いのRDBについてご確認ください。
Oracleでは、常にトランザクション処理を行っているため不要。
排他制御について
RDBへのアクセスを独占できなければ、排他制御を行わない場合、名寄せ処理の途中で、他ユーザにより元データが更新されることが起こりえます。
UPDATE Member SET Status='U' WHERE MemberID IN (4286,29153,30993) INSERT INTO Member VALUES (46071,'渡辺','京子','東京都国分寺市','090-7525-XXXX','A')
この場合上図のように、名寄せ結果に古いデータが混ざってしまいます。そのため排他制御が必要になります。1つの方法は「悲観的同時実行制御」です。これは、テーブルにフラグ列を追加し、データ読み出し時にフラグを立て、他のユーザの更新を禁止する方法です。
この方法であれば、名寄せ処理を確実に遂行できますが、他のユーザの更新処理は制限され、会員テーブルにアクセスする処理をすべて変更する必要があります。
もう一つの方法は「楽観的同時実行制御」です。これは、同時にアクセスされる可能性は低いだろうと考え、データ読み出し時には何も行わず、書き込み時にデータが更新されていないかチェックする方法です。データが変わっていなければ書き込みを実行し、変わっていれば書き込みを中断します。
UPDATE Member SET Status='U' WHERE MemberID=4286 AND Addr='東京都国分寺市' UPDATE Member SET Status='U' WHERE MemberID=29153 AND Addr='東京都小平市' UPDATE Member SET Status='U' WHERE MemberID=30993 AND Addr='東京都中野区' INSERT INTO Member VALUES (46071,'渡辺','京子','東京都国分寺市','090-7525-XXXX','A')
データが変わっていればUPDATE文は失敗するので(更新された行数が1でない)、その場合は処理を中断します。この例では住所のみデータのチェックを行っていますが、実際にはすべての列のデータをWHERE句の条件に加える必要があります。
なお、お使いのシステムの会員テーブルに「最終更新日時」列がある場合は、MemberID、最終更新日時、Statusのみの条件で代替できます。
今回は、この楽観的同時実行制御を実装するものとします。