データベーススペシャリスト 2014年 午後1 問02
データベースアクセスの同時実行制御に関する次の記述を読んで、設問1〜3に答えよ。
ソフトウェア開発会社であるK社は、イントラネットに会議室予約システムを構築し、運用している。
〔会議室予約システムの概要〕
会議室予約システムは、各社員の自席のPC及び会議室に設置されているタブレット端末で利用する。
会議室予約システムには社員番号でログインし、会議室番号、予約日、予約開始時刻、予約終了時刻を指定して会議室予約を行う。 予約開始時刻、予約終了時刻には30分単位の時刻を入力する。 また、会議室番号を指定して予約状況を確認したり、人数、日時を指定して空き会議室を検索したりすることができる。 複数の社員が、同じ会議室に対して重複する日時を指定して予約した場合は、最も早く実行された予約を予約成功とし、その他は予約失敗とする。
〔会議室予約システムのテーブル〕
会議室予約システムの主要なテーブルのテーブル構造、概要は、図1、表1のとおりである。



〔RDBMSのトランザクション制御〕
会議室予約システムで使用するRDBMSのトランザクションのISOLATIONレベルはREAD COMMITTEDであり、行単位でロックをかける。 データ参照時には共有ロックをかけ、参照終了時に解放する。 データ更新時には専有ロックをかけ、トランザクション終了時に解放する。 専有ロックがかかっている間、他のトランザクションからの対象行の参照、更新は専有ロックの解放待ちとなる。
〔会議室予約システムでの検索〕
会議室予約システムで空き会議室の検索結果一覧を表示する際に必要な情報を得るために実行するSQL文の例を図2に示す。
なお、図2中のホスト変数のhv1は予約希望日、hv2は予約希望開始時刻、hv3は予約希望終了時刻を表す。
↩設問1(1)
↩設問1(1)〔会議室予約システムでの予約処理〕
会議室予約システムの予約処理内容は、図3のとおりである。
なお、図3中のホスト変数のhv1は指定予約日、hv2は指定予約開始時刻、hv3は指定予約終了時刻、hv4は指定会議室番号、hv5は予約者の社員番号を表す (以降の図5、図7でも同様とする)。
〔会議室予約システムでのダブルブッキングの検証〕
会議室予約システムにおいてダブルブッキングが発生した。 K社ではその原因を突き止めるために当日の状況を基に、次の例を用いて図3の予約処理内容の検証を行った。
(例)
・会議室123に対する2014年4月1日の予約を行う。 その日の予約は終日入っていなかった。
・Aさん,Bさん,Cさん、Dさん,Eさんの5人が、それぞれ次の時間帯を指定した。
Aさん: 11時00分〜12時00分
Bさん: 11時00分〜13時00分
Cさん: 11時30分〜13時00分
Dさん: 9時00分〜11時00分
Eさん: 8時00分〜18時00分
(検証)
5人の予約処理の実行が重ならない場合と、重なった場合について、それぞれ(1) 、(2) で検証した。
(1) Aさん, Bさん, Cさん, Dさん, Eさんの順番で、予約処理の実行が重ならない場合の結果を検証して、表2を作成した。
(2) Aさん, Bさん, Cさん, Dさん, Eさんのうち、2人ずつの全ての組合せに対して、先行、後続を入れ替えて、予約処理の実行が重なった場合の結果を検証して、表3を作成した。 表3では、先行の予約処理が図3①の処理を実行した後に、後続の予約処理が図3①の処理を実行、その後に先行の予約処理が図3②の処理を実行することを想定している。
なお、後続の予約処理は、先行の予約処理を追い抜くことはないものとする。
(1) 、(2) の検証から、ダブルブッキングとなる理由を次のように結論付けた。
同じ会議室に対して、予約処理の実行が重なった時に、次の二つの条件が成立する場合にダブルブッキングが発生する。
・(r)が重なる。
・(s)が異なる。
〔会議室予約システムの改良案〕
ダブルブッキングを防ぐために図4表4の “日別予約管理” テーブルを追加し、予約処理内容を図5のように改良することを検討した。


〔会議室予約システムの改良結果〕
図5の⑤〜⑦の実行時に、ディスク容量不足などのエラーが発生して、予約処理のトランザクションが中断してしまうと問題が生じることが分かったので、〔会議室予約システムの改良案〕 は不採用とし、“会議室予約” テーブルを図6のように変更した。

会議室予約を行う予約単位をコマと呼び、1コマは30分とする。 図6の “会議室予約” テーブルでは、各コマを0時00分から30分間隔で設定する。 予約受付対象の全てのコマはあらかじめ登録しておく。 該当するコマが予約済みか否かを予約済フラグで識別し、予約済みであれば ‘Y'、予約済みでなければ 'N' とする。 例えば,10時00分〜11時30分を予約済みとする場合、予約開始時刻が10時00分、10時30分、11時00分の3コマの予約済フラグを 'Y' に更新する。
変更した “会議室予約” テーブルを使用して、予約処理内容を図7のように変更した。
なお、図7中のホスト変数のcntはコマ数を表す。 また、図7中のユーザ定義関数について次に示す。
・PERIODSTART関数は、コマの終了時刻を与えて、コマの開始時刻を求めるユーザ定義関数とする。 例えば、11時30分を指定した場合、11時00分が返却される。
・PERIODCOUNT関数は、開始時刻と終了時刻を与えて、含まれるコマ数を求めるユーザ定義関数とする。 例えば、10時00分と11時30分を指定した場合,3が返却される。
・PERIODNEXT関数は、指定の時刻とコマ数を与えて、指定の時刻からコマ数だけ後にずらしたコマの開始時刻を求めるユーザ定義関数とする。 例えば、10時00分とコマ数2を指定した場合、11時00分が返却される。
設問1:会議室予約システムについて、(1)〜(4)に答えよ。
問題文を見る(1)図2中のSQL文の(a)〜(c)に入れる適切な字句を答えよ。
模範解答
a:NOT EXISTS
b:<
c:>
解説
解答の論理構成
- 検索要件の整理
【問題文】には「複数の社員が、同じ会議室に対して重複する日時を指定して予約した場合は、最も早く実行された予約を予約成功とし、その他は予約失敗とする。」とあります。したがって“空き会議室検索”は重複予約が1件も存在しない会議室のみを抽出する処理です。 - サブクエリの役割
図2の SQL は外側で SELECT * FROM 会議室 X を行い、内側で相関をとる形です。「対象会議室が重複予約を持たない」ことを保証するため、外側 WHERE 句では“既存予約が存在しない”ことを表す必要があります。つまり (a) は NOT EXISTS です。 - 重複判定条件
重複(オーバーラップ)は次の条件で判定できます。- 既存予約の開始時刻 < 希望終了時刻
- 既存予約の終了時刻 > 希望開始時刻
いずれか一方が等号の場合、同一コマ境界での「背中合わせ」を許可できます。本問では“30分単位”で区切られており背中合わせは問題ないため、厳密に < と > を採用します。よって (b) は <、(c) は > となります。
誤りやすいポイント
- EXISTS と NOT EXISTS の選択を逆にし、空いていない会議室ばかり抽出してしまう。
- オーバーラップ条件を <=・>= で書き、前後がぴったり接する予約まで除外してしまう。
- BETWEEN :hv2 AND :hv3を使い、重複判定を1条件で済ませようとしても正しく表現できない。
FAQ
Q: NOT IN ではだめですか?
A: 相関サブクエリで行ごとに判断するためには NOT EXISTS が最も効率的です。NOT IN だとサブクエリのNULL取り扱いで漏れが生じる恐れがあります。
A: 相関サブクエリで行ごとに判断するためには NOT EXISTS が最も効率的です。NOT IN だとサブクエリのNULL取り扱いで漏れが生じる恐れがあります。
Q: 等号を含めるべきケースは?
A: 1分単位・秒単位などで管理し、かつ「開始=終了」の瞬間予約を許可しない運用では <=・>= に変えることがあります。本システムは “30分単位” かつ背中合わせを許容するため <・> で問題ありません。
A: 1分単位・秒単位などで管理し、かつ「開始=終了」の瞬間予約を許可しない運用では <=・>= に変えることがあります。本システムは “30分単位” かつ背中合わせを許容するため <・> で問題ありません。
Q: READ COMMITTEDなのに検索だけでロック競合しませんか?
A: 共有ロックは読み取り中のみ保持されるので競合は一時的です。ここでは“空き検索”で更新を伴わないため NOT EXISTS 判定自体が同時実行上のボトルネックにはなりにくいです。
A: 共有ロックは読み取り中のみ保持されるので競合は一時的です。ここでは“空き検索”で更新を伴わないため NOT EXISTS 判定自体が同時実行上のボトルネックにはなりにくいです。
関連キーワード: NOT EXISTS, サブクエリ、重複検出、READ COMMITTED, 共有ロック
設問1:会議室予約システムについて、(1)〜(4)に答えよ。
問題文を見る(2)図3中の②は、行が挿入できないことでダブルブッキングとならないように制御している。 その制御で行が挿入できない理由を30字以内で述べよ。
模範解答
・主キーの値が重複するから
・会議室番号、予約日、予約開始時刻が同じ行が存在するから
解説
解答の論理構成
- 一意制約の確認
- 【問題文】「会議室番号、予約日、予約開始時刻で会議室予約を一意に識別する。」
→ この3列が主キー(もしくは UNIQUE 制約)であると読み取れる。
- 【問題文】「会議室番号、予約日、予約開始時刻で会議室予約を一意に識別する。」
- INSERT 処理②の動作
-
図3②のSQLは
sql INSERT INTO 会議室予約 (会議室番号, 予約日, 予約開始時刻, 予約終了時刻, 社員番号) VALUES(:hv4, :hv1, :hv2, :hv3, :hv5); -
既に同じ「会議室番号、予約日、予約開始時刻」の行が存在すると、主キー一意性が破られDBが拒否。
-
- ダブルブッキング抑止との関係
- 条件①で重複チェックをしているが、同時実行で別トランザクションが先に INSERT すると②の直前チェックをすり抜ける可能性がある。
- その場合でも一意制約が最後の砦となり、重複キーエラーで行が挿入できず予約失敗となるためダブルブッキングは防止される。
- 以上より、「主キー重複(重複キー)による一意制約違反」が挿入失敗の直接原因である。
誤りやすいポイント
- 「予約終了時刻」まで含めて重複チェックされると誤解しやすい。実際の主キー列に含まれない。
- ロック待ちやデッドロックが原因だと思い込むケース。あくまで制約違反が直接要因。
- SELECT①が空なら必ず INSERT 成功と決めつけ、同時実行による race condition を見落とす。
FAQ
Q: 主キーに含まれない「予約終了時刻」が違う場合でも INSERT は失敗しますか?
A: はい、主キー列「会議室番号、予約日、予約開始時刻」が同じなら失敗します。予約終了時刻は主キーに含まれないため違いは無関係です。
A: はい、主キー列「会議室番号、予約日、予約開始時刻」が同じなら失敗します。予約終了時刻は主キーに含まれないため違いは無関係です。
Q: READ COMMITTEDで共有ロックを使っているのに重複キーが発生するのはなぜ?
A: SELECT① の共有ロックは参照終了時に解放されるため、同時実行中に別トランザクションが INSERT 可能となり、競合が起き得ます。このとき主キー制約が重複を最終的に検知します。
A: SELECT① の共有ロックは参照終了時に解放されるため、同時実行中に別トランザクションが INSERT 可能となり、競合が起き得ます。このとき主キー制約が重複を最終的に検知します。
Q: 一意制約エラーはアプリケーション側でハンドリングすべきですか?
A: はい、INSERT 失敗をトランザクション内で検知し、ロールバックやユーザ通知を行うことで整合性を保ちます。
A: はい、INSERT 失敗をトランザクション内で検知し、ロールバックやユーザ通知を行うことで整合性を保ちます。
関連キーワード: 主キー制約、一意性違反、競合解決、行ロック、同時実行制御
設問1:会議室予約システムについて、(1)〜(4)に答えよ。
問題文を見る模範解答
d:×
e:①
f:○
g:
h:×
i:①
j:×
k:②
l:△
m:
n:○
o:
p:△
q:
解説
解答の論理構成
-
ロックの前提確認
問題文に「ISOLATIONレベルは READ COMMITTED であり、 行単位でロックをかける。データ参照時には共有ロックをかけ、参照終了時に解放する。」とあります。したがって- SELECT(図3①)の共有ロックは直ちに解放
- INSERT(図3②)の専有ロックはトランザクション終了まで保持
となります。
-
表2 ― 実行が重ならない場合
予約処理が直列なので、単純に時間帯の重なりで成否が決まります。- A:最初なので ○
- B:11時00分〜13時00分 はAと重なるため、図3①で重複行が見付かり ×/①
- C:11時30分〜13時00分 もAと重なるため ×/①
- D:9時00分〜11時00分 はAの開始と接しているだけで重ならないので ○
- E:8時00分〜18時00分 はAと重なるため ×/①
-
表3 ― 2人の処理が重なった場合
想定は「先行が図3①を実行後、後続が図3①を実行し、その後に先行が図3②を実行」です。ここでカギになるのは「先行の SELECT で取得した共有ロックがすぐ解放される」点です。以上より- (j) ×, (k) ② (先行B)
- (l) △, (m) - (先行C)
- (n) ○, (o) - (先行D)
- (p) △, (q) - (先行E)
-
まとめ
ダブルブッキングは、図3①で互いの予約を検出できず、主キーも衝突しない場合に発生します。問題文の結論「同じ会議室に対して、予約処理の実行が重なった時に、
・予約時間帯が重なる
・予約開始時刻が異なる
場合にダブルブッキングが発生する。」と完全に一致します。
誤りやすいポイント
- READ COMMITTED では SELECT 後すぐにロックが解放されることを忘れ、共有ロックが継続していると誤解する。
- 主キーが「会議室番号, 予約日, 予約開始時刻」であるため、開始時刻が違えば INSERT は競合しない点を見落とす。
- 図3②で一意制約違反が起きるのは「開始時刻が同じ」ときだけで、時間帯全体の重なりとは別である。
FAQ
Q: 共有ロックが解放された後に別トランザクションが同じ行を更新すると、先行のコミット時に矛盾は起きませんか?
A: 先行はまだ INSERT を実行していないのでロック対象行自体が存在しません。したがって矛盾は発生せず、後続の処理が主キー重複で失敗するかどうかは INSERT のタイミングで決まります。
A: 先行はまだ INSERT を実行していないのでロック対象行自体が存在しません。したがって矛盾は発生せず、後続の処理が主キー重複で失敗するかどうかは INSERT のタイミングで決まります。
Q: 図3①の SELECT を SELECT … FOR UPDATE にすれば防げますか?
A: 防げますが、全重複時間帯をロックできるように検索条件を工夫する必要があります。またロック範囲が広がり、同時実行性が大きく低下する点に注意が必要です。
A: 防げますが、全重複時間帯をロックできるように検索条件を工夫する必要があります。またロック範囲が広がり、同時実行性が大きく低下する点に注意が必要です。
Q: 図7の改良案がロールバックで問題になるのはなぜですか?
A: 途中でエラーが発生すると、複数コマの一部だけが 予約済フラグ='Y' のまま残る可能性があるためです。整合性を維持するために全コマを一括して更新・ロールバックする仕組みが必須になります。
A: 途中でエラーが発生すると、複数コマの一部だけが 予約済フラグ='Y' のまま残る可能性があるためです。整合性を維持するために全コマを一括して更新・ロールバックする仕組みが必須になります。
関連キーワード: READ COMMITTED, 共有ロック, 一意制約, ダブルブッキング, トランザクション整合性
設問1:会議室予約システムについて、(1)〜(4)に答えよ。
問題文を見る(4)ダブルブッキングとなる条件の(r)、(s)に入れる適切な字句を答えよ。
模範解答
r:時間帯
s:予約開始時刻
解説
解答の論理構成
- 原文の結論部
同じ会議室に対して、予約処理の実行が重なった時に、次の二つの条件が成立する場合にダブルブッキングが発生する。
・(r)が重なる。
・(s)が異なる。 - 「重なる」対象は何か
- 「異なる」対象は何か
-
図3①の SELECT 条件は
sql WHERE 会議室番号 = :hv4 AND 予約日 = :hv1 AND 予約開始時刻 (b) :hv3 AND 予約終了時刻 (c) :hv2すなわち “予約開始時刻” と “予約終了時刻” を比較して重複チェックをする。 -
もし両者の 予約開始時刻 が同一なら行ロック競合により後続がブロックされるが、異なれば「READ COMMITTED+共有ロック即解放」のため互いを検知できない。
-
後続が INSERT を実行した時点で幻読が発生し、ダブルブッキングが確定する。
-
よって (s) は「予約開始時刻」。
-
- 以上より
- (r)=時間帯
- (s)=予約開始時刻
誤りやすいポイント
- 「同じ時間帯なら必ず開始時刻も同じ」と思い込む。30分単位なので“11:00〜12:00”と“11:30〜13:00”のように開始時刻がズレても重複します。
- READ COMMITTEDだから安全と勘違いし、幻読のリスクを見落とす。
- (s) に「予約終了時刻」を入れてしまう。終了時刻が同じでも開始時刻がズレていれば SELECT で漏れるケースがある点に注意。
FAQ
Q: なぜ「予約終了時刻」が違ってもダブルブッキングと断定しないのですか?
A: 終了時刻が違っても開始時刻が同じなら行ロックでブロックされるため、後続処理は SELECT の時点で検知できます。問題は開始時刻がズレて SELECT 範囲から漏れるケースです。
A: 終了時刻が違っても開始時刻が同じなら行ロックでブロックされるため、後続処理は SELECT の時点で検知できます。問題は開始時刻がズレて SELECT 範囲から漏れるケースです。
Q: ISOLATIONレベルを「SERIALIZABLE」に変えれば解決しますか?
A: はい、幻読を防げるので今回の競合は起こりません。ただし性能面でロック競合が増えるため、問題文ではアプリ側のロジック修正を優先しています。
A: はい、幻読を防げるので今回の競合は起こりません。ただし性能面でロック競合が増えるため、問題文ではアプリ側のロジック修正を優先しています。
Q: 30分単位でコマ分割する方式(図7)のメリットは?
A: 各コマに行ロックを直接かけるため、任意の時間帯を UPDATE だけで確実に確保でき、重複チェックと登録を一体化できます。複雑な SELECT 条件が不要になり、幻読を根本的に排除できます。
A: 各コマに行ロックを直接かけるため、任意の時間帯を UPDATE だけで確実に確保でき、重複チェックと登録を一体化できます。複雑な SELECT 条件が不要になり、幻読を根本的に排除できます。
関連キーワード: 幻読、排他制御、行ロック、READ COMMITTED, 時間帯重複
設問2:〔会議室予約システムの改良案〕 について、(1)〜(3)に答えよ。
問題文を見る(1)同じ日に、同じ会議室に対して予約が集中する状況を想定すると、図5中②でコミットを行わない場合、スループットが低下する。 その原因となる処理を図5中の番号で答えよ。また、原因を25字以内で述べよ。
模範解答
処理番号:①
原因:多数の専有ロックの解放待ちが発生する。
解説
解答の論理構成
- 図5の流れを確認
- 「① 予約処理中フラグをUPDATE文で ‘Y’ に更新する」
- 「② コミットする」
- RDBMSのロック仕様
- 「データ更新時には専有ロックをかけ、トランザクション終了時に解放する」
- 「専有ロックがかかっている間、他のトランザクションからの対象行の参照、更新は専有ロックの解放待ちとなる」
- コミット省略時の影響
- ②を実行しないとトランザクションが終了しない
- よって①で更新した“日別予約管理”の行は専有ロック状態のまま
- 同時アクセスが集中する条件
- 設問が示す「同じ日に、同じ会議室に対して予約が集中」すると、多数のトランザクションが同じ行を読もうとする
- 結果
- 先行トランザクションがコミットするまで後続はロック待ち
- CPU・I/Oが遊んでしまいスループットが低下
- よって処理番号は「①」、原因は「多数の専有ロックの解放待ちが発生する」となる。
誤りやすいポイント
- 「図5では②がコミットだから①が原因では?」と逆に考えてしまう
→ コミットを省く条件下では①がロック保持元になる点に注意。 - READ COMMITTEDだから参照はブロックされないと思い込む
→ 専有ロック中は参照も待たされる仕様が明示されている。 - “日別予約管理”を読み取り専用だと誤認
→ 実際には①で更新しているためロックが掛かる。
FAQ
Q: READ COMMITTEDでも共有ロックはすぐ解放されるのでは?
A: 共有ロックは参照終了時に解放されますが、①は更新なので専有ロックとなり、コミットしない限り解放されません。
A: 共有ロックは参照終了時に解放されますが、①は更新なので専有ロックとなり、コミットしない限り解放されません。
Q: 行ロックだから競合は少ないのでは?
A: 集中する条件では全員が同じ会議室番号・予約日を更新するため、狙う行は1行。行ロックでも競合は激しくなります。
A: 集中する条件では全員が同じ会議室番号・予約日を更新するため、狙う行は1行。行ロックでも競合は激しくなります。
Q: 専有ロック待ち中のトランザクションはタイムアウトしますか?
A: 多くのRDBMSでは待機し続けます(設定によってはタイムアウト可)。問題文では待ち続ける前提です。
A: 多くのRDBMSでは待機し続けます(設定によってはタイムアウト可)。問題文では待ち続ける前提です。
関連キーワード: 専有ロック、行ロック、コミット、スループット、トランザクション制御
設問2:〔会議室予約システムの改良案〕 について、(1)〜(3)に答えよ。
問題文を見る(2)図5中の ④において、⑧に進む処理は、速やかに予約失敗を検知するために行っている。この処理はどのような状況を想定して行っているか。20字以内で述べよ。
模範解答
予約対象に予約が入っている状況
解説
解答の導き方
図5の④には「①で予約処理中フラグを更新できておらず、③で結果行がある場合は、予約失敗として⑧に進む」と明記されています。これが設問の根拠です。
まず①の処理を見ると、①は「UPDATE 日別予約管理 SET 予約処理中フラグ = 'Y' WHERE 会議室番号 = :hv4 AND 予約日 = :hv1 AND 予約処理中フラグ = 'N'」です。WHERE句により、予約処理中フラグが 'N' の行だけが 'Y' に更新されます。したがって「更新できておらず」とは、UPDATE 実行後の影響行数が0であり、対象行の予約処理中フラグが既に 'Y' であった(=他者が既にフラグを立てている)ことを意味します。
次に③の処理は会議室予約テーブルを調べる SELECT 文であり、「結果行がある」ことは会議室予約テーブルに重複する予約が存在することを示します。
以上を組み合わせると、①でフラグを立てられなかった(=他者がフラグを立てている)かつ③で既に会議室予約テーブルに該当予約が存在するということは、既に誰かが当該予約対象を確保していることを意味します。したがって図5の④が想定している状況は「予約対象に予約が入っている状況」です。
よって想定している状況は「予約対象に予約が入っている状況」です。
(補足)RDBMSは問題文のとおり ISOLATION レベルが READ COMMITTED で、更新時に専有ロックを取得します。したがって UPDATE がロック待ちで一時停止する可能性はありますが、ここでの「更新できておらず」は通常「UPDATE 実行後に影響行数が0であった」ことを指す点に注意してください。
誤りやすいポイント
- 「①で更新できておらず」を単純に「常にロック待ち」と解釈する誤り。ロック待ち(UPDATE がブロックされている)と、UPDATE が完了して影響行数が0であった(既にフラグが 'Y')状況は区別する必要があります。
- ③の「結果行がある」を日別予約管理のフラグと混同する誤り。③は会議室予約テーブルの重複予約を調べる処理であり、結果行は実際の予約の存在を示します。
- フローの分岐を逆に覚える誤り。図5では「①で更新できておらず、③で結果行がない場合は①に戻る」ので、更新できなかったがまだ予約が入っていない(処理中の可能性がある)場合は再試行する点を押さえておくこと。
FAQ
Q: 「更新できておらず」は具体的にどのような状態ですか?
A: ①の UPDATE は WHERE によって予約処理中フラグが 'N' の行だけを 'Y' にするため、UPDATE 実行後の影響行数が0であれば「更新できておらず」と判定されます。これは通常「既にフラグが 'Y' にされている(他者が先に処理を始めたか完了した)」ことを意味します。ロック待ちで一時停止している場合は UPDATE が完了するまで待機するため、図中の判断とは別扱いです。
A: ①の UPDATE は WHERE によって予約処理中フラグが 'N' の行だけを 'Y' にするため、UPDATE 実行後の影響行数が0であれば「更新できておらず」と判定されます。これは通常「既にフラグが 'Y' にされている(他者が先に処理を始めたか完了した)」ことを意味します。ロック待ちで一時停止している場合は UPDATE が完了するまで待機するため、図中の判断とは別扱いです。
Q: この仕組みでロック待ちや競合は発生しませんか?
A: 発生する可能性はあります。問題文にあるように ISOLATION レベルは READ COMMITTED で、更新時に専有ロックを取得します。図5は①でUPDATE後に②でコミットする設計になっており、ロック保持時間を短くして競合を減らす工夫をしていますが、完全に待ちが起きないわけではありません。
A: 発生する可能性はあります。問題文にあるように ISOLATION レベルは READ COMMITTED で、更新時に専有ロックを取得します。図5は①でUPDATE後に②でコミットする設計になっており、ロック保持時間を短くして競合を減らす工夫をしていますが、完全に待ちが起きないわけではありません。
関連キーワード: トランザクション隔離レベル、行レベルロック、排他ロック、共有ロック
設問2:〔会議室予約システムの改良案〕 について、(1)〜(3)に答えよ。
問題文を見る(3)〔会議室予約システムの改良結果〕 で述べている 〔会議室予約システムの改良案〕で生じる問題について、どのテーブルがどのような状態になるかを40字以内で述べよ。 また、それによって引き起こされる問題を30字以内で述べよ。
模範解答
状態:“日別予約管理”テーブルの予約処理中フラグが'Y'のままとなる。
問題:その日付、その会議室を誰も予約できなくなる。
解説
解答の論理構成
- 図5①の更新
UPDATE 日別予約管理 SET 予約処理中フラグ = 'Y' で対象行を占有。 - 図5②でコミット
ここで‘Y’が確定する。 - 図5⑤〜⑦の途中エラー想定
【問題文】「ディスク容量不足などのエラーが発生して、予約処理のトランザクションが中断」と記載。 - フラグ復旧漏れ
エラーにより図5⑥の UPDATE … 'N' が実行されず、フラグは‘Y’のまま。 - 影響
後続予約は図5①の … 予約処理中フラグ = 'N' 条件で更新不可となり、「その日付、その会議室」を誰も予約できない。
誤りやすいポイント
- トランザクションは②で一度コミット済みなので、後続ロールバックでは‘Y’を戻せない事実を見落とす。
- “会議室予約”テーブルの行ロックを考慮し、フラグ残留問題と混同する。
- “ディスク容量不足”=ロールバックと決め付け、実際には途中で異常終了するシナリオを誤読する。
FAQ
Q: なぜ②でコミットしているのに再度⑦でコミットが必要なのですか?
A: 一連の予約処理を二段階に分けており、①②は“日別予約管理”の確保、⑤〜⑦は実際の予約登録を担うためです。確保後に再度トランザクションを開始し、登録完了時に⑦で確定します。
A: 一連の予約処理を二段階に分けており、①②は“日別予約管理”の確保、⑤〜⑦は実際の予約登録を担うためです。確保後に再度トランザクションを開始し、登録完了時に⑦で確定します。
Q: 途中でフラグを‘N’に戻す他の方法はありますか?
A: タイムアウト検知やバッチでの一括リセットが考えられますが、即時性がなく二重予約を防げません。根本解決には図7のような行ロック方式へ設計変更する方が安全です。
A: タイムアウト検知やバッチでの一括リセットが考えられますが、即時性がなく二重予約を防げません。根本解決には図7のような行ロック方式へ設計変更する方が安全です。
Q: READ COMMITTEDでも同時実行制御は十分では?
A: 図5案ではアプリケーションレベルのフラグ管理に依存しており、RDBMSの行ロックとは別に“フラグ残留”という論理的な不整合が発生します。Isolation Levelだけでは解決できません。
A: 図5案ではアプリケーションレベルのフラグ管理に依存しており、RDBMSの行ロックとは別に“フラグ残留”という論理的な不整合が発生します。Isolation Levelだけでは解決できません。
関連キーワード: トランザクション制御、コミット、ロールバック、排他制御、資源ロック
設問3:〔会議室予約システムの改良結果〕 について、(1)、(2)に答えよ。
問題文を見る(1)図7中の(t)に入れる適切な字句を答えよ。
模範解答
t:COUNT(*)
解説
解答の導き方
まず図7の該当SQLの目的と構成を押さえます。図7には「SELECT 会議室番号 FROM 会議室予約 WHERE 会議室番号 = :hv4 AND 予約日 = :hv1 AND 予約開始時刻 BETWEEN :hv2 AND PERIODSTART(:hv3) AND 予約済フラグ = 'N' GROUP BY 会議室番号 HAVING (t) = PERIODCOUNT(:hv2, :hv3)」とあります。
-
WHERE 節に「予約済フラグ = 'N'」がある点に注目します。図6の説明にあるように「予約済フラグで識別し、予約済みであれば 'Y'、予約済みでなければ 'N' とする」ため、この SELECT が返す行は要求区間に含まれる未予約(空き)コマの行です。つまりSELECTは「要求期間内で未予約のコマを列挙する」処理です。
-
図7の説明にある通り「PERIODCOUNTは、開始時刻と終了時刻を与えて、含まれるコマ数を求める」ため、PERIODCOUNT(:hv2, :hv3) は要求された予約で必要なコマ数(期待する行数)を返します(例: 10時00分〜11時30分 → 3)。
-
GROUP BY 会議室番号 と HAVING (t) = PERIODCOUNT(...) の組合せは、会議室ごとに抽出された未予約のコマの個数が「要求で必要なコマ数」と一致するかを判定する意図です。したがって (t) は「グループ内の行数」を返す集約式でなければなりません。
-
グループ内の行数を正確に得る集約関数として適切なのは COUNT() です。COUNT() はグループ内の総行数(NULLの有無にかかわらず行そのものの数)を返すため、選択された未予約コマの個数と一致します。したがって (t) に入るべき字句は COUNT(*) です。
結論: (t) = COUNT(*)
(補足)COUNT(1) は実務上 COUNT(*) と同様に行数を返す記法であり機能的に同等ですが、特定列を指定する COUNT(列名) はその列がNULLの行を除外して数えるため、本件のように未予約コマで社員番号等がNULLになっている可能性がある場合は不適切です。
誤りやすいポイント
-
COUNT(列名) を使う誤り
社員番号などを COUNT(社員番号) で数えるとNULLを除外してしまい、期待するコマ数と合わなくなる。 -
HAVING と WHERE の混同
集約結果(COUNT 等)による条件は HAVING で指定する必要があり、WHERE では集約値を直接使えない。 -
PERIODSTARTの意味の取り違え
PERIODSTART(:hv3) は要求終端時刻から最後のコマの開始時刻を求める関数なので、範囲指定にそのまま終端時刻を使うと最後のコマを正しく含められない可能性がある。 -
COUNT() の扱いの誤認
COUNT() が行数を返すことは押さえるが、COUNT(1) が機能的に同等であることを知らないと混乱する。
FAQ
Q: COUNT(1) を使っても良いですか?
A: はい。COUNT(1) は非NULL定数を評価して行数を数えるため、実務上は COUNT() と同じ結果になります。ただし可読性の観点や試験での期待により COUNT() を用いるのが無難です。
A: はい。COUNT(1) は非NULL定数を評価して行数を数えるため、実務上は COUNT() と同じ結果になります。ただし可読性の観点や試験での期待により COUNT() を用いるのが無難です。
Q: なぜ WHERE で「PERIODCOUNT(:hv2, :hv3) = (t)」のように比較できないのですか?
A: WHERE は行レベルのフィルタであり、COUNT のような集約値は GROUP BY による集計後に計算されます。集約結果に対する条件は HAVING で指定する必要があります。
A: WHERE は行レベルのフィルタであり、COUNT のような集約値は GROUP BY による集計後に計算されます。集約結果に対する条件は HAVING で指定する必要があります。
Q: 部分的に空きのコマがあってもその会議室は選ばれますか?
A: 図7のロジックは「会議室ごとに抽出された未予約コマの数が必要なコマ数と一致する」ことを確認しているため、部分的にしか空いていない会議室は除外されます。つまり全コマが空いている会議室だけが選ばれます。
A: 図7のロジックは「会議室ごとに抽出された未予約コマの数が必要なコマ数と一致する」ことを確認しているため、部分的にしか空いていない会議室は除外されます。つまり全コマが空いている会議室だけが選ばれます。
関連キーワード: 集約関数、GROUP BY、HAVING句、COUNT関数、NULLの扱い
設問3:〔会議室予約システムの改良結果〕 について、(1)、(2)に答えよ。
問題文を見る(2)図7中の②において、更新がなくて予約失敗となるのはどのような状況か。 40字以内で述べよ。
模範解答
①の終了後、②の終了までの間に、他の予約処理が範囲内のコマに予約を入れた。
解説
解答の論理構成
- 図7①
SELECT ... 予約済フラグ = 'N' ... HAVING (t) = PERIODCOUNT(:hv2, :hv3) で重複がないことを確認します。SELECT 終了時点で共有ロックは解放されます(〔RDBMS のトランザクション制御〕「データ参照時には共有ロックをかけ、参照終了時に解放する。」)。 - 図7②
UPDATE ... SET 予約済フラグ = 'Y' ... WHERE ... 予約済フラグ = 'N' を対象コマ数分繰り返します。もし1回でも “更新行がなかった” 場合にロールバックし予約失敗になります(図7②)。 - 競合シナリオ
①が終わり共有ロックが外れた隙に、別の予約処理が同一コマを 予約済フラグ = 'Y' に変更すると、②の WHERE ... 予約済フラグ = 'N' 条件に合致せず更新0件になります。 - 結論
「①の終了後、②の終了までの間に、他の予約処理が範囲内のコマに予約を入れた」ときに更新0件 → 予約失敗となります。
誤りやすいポイント
- READ COMMITTED では共有ロックはSELECT終了後すぐ外れる点を忘れ、ロックでブロックされると誤解する。
- ②は “全部の更新が成功したか” を見るループであり、1コマでも更新0件なら即ロールバックする仕様を見落とす。
- PERIODCOUNTやPERIODNEXTの処理ロジックに意識が向きすぎ、ロックタイミングの問題を見逃す。
FAQ
Q: READ COMMITTED以外のISOLATIONレベルなら防げますか?
A: 例えばSERIALIZABLEにすればダブルブッキングは防げますが、ロック待ち増加で性能が劣化するため、本システムではアプリ側で制御しています。
A: 例えばSERIALIZABLEにすればダブルブッキングは防げますが、ロック待ち増加で性能が劣化するため、本システムではアプリ側で制御しています。
Q: コマ単位方式にしたのにまだダブルブッキングが起こるのですか?
A: 仕様上、②で更新0件になるとロールバックして予約失敗となるのでダブルブッキング自体は起きません。ただし「予約しようとしたが他トランザクションに先を越される」ケースは発生します。
A: 仕様上、②で更新0件になるとロールバックして予約失敗となるのでダブルブッキング自体は起きません。ただし「予約しようとしたが他トランザクションに先を越される」ケースは発生します。
関連キーワード: READ COMMITTED, 行ロック、共有ロック、排他更新、トランザクション競合








