データベーススペシャリスト 2018年 午後1 問02
データベースでの制約の実装に関する次の記述を読んで、設問1〜3に答えよ。
総合商社のY社は、人事情報管理にRDBMSを用いている。
〔RDBMSの主な仕様〕
人事情報管理データベースに用いているRDBMSの主な仕様は、次のとおりである。
1.参照制約
参照制約では、挙動モードと検査契機モードを指定できる。
(1) 挙動モード
挙動モードには、次の二つがある。
① NO ACTION : 参照先のテーブルの行を削除又は更新したとき、参照元のテーブルの行に対して何もしない。
② CASCADE : 参照先のテーブルの行を削除又は更新したとき、参照元のテーブルの行にも削除又は更新を連鎖させる。
(2) 検査契機モード
検査契機モードには、次の二つがある。
① 即時モード: SQL実行終了ごとに、対象となる全ての行の実行結果に対して、制約を検査する。
② 猶予モード: トランザクション終了時に、トランザクション内の全てのSQLを実行した結果に対して、制約を検査する。
2.トリガ
テーブルに対する変更操作 (挿入・更新・削除)を契機に、あらかじめ定義された処理を、操作対象の行ごとに実行する。 実行タイミング (挿入・更新・削除の前又は後)、列値による実行条件を定義することができる。 ただし、実行タイミングを挿入・更新・削除の前として定義したトリガの処理の中で、テーブルに対する変更操作を行うことはできない。
〔人事情報管理データベースのテーブル〕
人事情報管理データベースの主なテーブルのテーブル構造は、図1のとおりである。索引は、主キーだけに定義されている。

〔人事情報管理の業務概要〕
人事情報管理担当者は、業務上のイベント発生時にテーブルの更新を行うとともに、社内の各部署からの情報提供要求に対応するために、SQLで検索した結果をレポートにして提供している。 業務上の主なイベントとテーブルの更新内容、及びレポート提供の一例は次のとおりである。
1.従業員の退職
満65歳で定年退職及び従業員の自己都合による退職が随時発生する。 定年退職の場合、退職年月日は、満65歳の誕生日の前日である。 従業員を “従業員” テーブルに登録するときに、退職年月日に定年退職の年月日を設定する。 自己都合による退職の場合、退職年月日は申告された日に更新する。
退職すると、“従業員” テーブルの退職フラグを、あらかじめ設定している '0'(在籍)から '1' (退職) に更新する。 この処理は、退職年月日の当日に行う。給与計算は毎月20日締めで行う。 また、毎月25日に、“従業員” テーブル及び“従業員家族” テーブルから、当月の20日までに退職した従業員の行を削除する。削除した行を別テーブルに保存するために、実行タイミングを“従業員”テーブルの削除の後としたトリガを定義している。 トリガに定義している処理は、次のとおりである。
・削除した “従業員” テーブルの行を別テーブルに挿入する。
・“従業員家族” テーブルのその従業員の家族の行について、別テーブルに挿入し、その後削除する。
毎月25日に行を削除するための、三つの述語から成るSQLの構文は表1のとおりである。
2.従業員家族構成の変更
従業員ごとの事由によって、家族の増減、扶養対象者数の変更などが随時発生する。このとき、“従業員家族” テーブルへの行挿入、主キー以外の列値の更新(扶養フラグは、扶養対象者でない場合は '0'、扶養対象者の場合は '1' に更新)、行削除を行う。
3.定期的な組織変更及び人事異動
毎年4月1日及び10月1日に組織変更及び人事異動を実施している。組織変更では、部署の新設、廃止が発生する。 このとき、“部署” テーブルに対する行挿入、行削除を行う。
人事異動で、従業員が所属する部署が変更になった場合、“従業員” テーブルの部署コードの更新を行う。
4.レポート提供の一例
福利厚生担当者から人事情報管理担当者に対して、従業員家族向けのレクリエーション企画のために、部署コード順に2007年1月1日以降に生まれた扶養対象者をもつ従業員数と扶養対象者数の一覧表が欲しいとの依頼があった。 その例を図2に示す。
人事情報管理担当者は、表2のSQL文を用いて、図2の一覧表を作成した。

〔人事情報管理データベースで発生している問題点〕
定型的な業務上のイベントはアプリケーションプログラムで実装している。 しかし、組織変更及び人事異動のイベントで、部署コードなどの更新する列の内容が変動するものは、次のような運用を行っている。
・人事情報管理担当者が、イベント別に用意してある更新SQL文のバッチジョブを利用して、業務上のイベントに対応したテーブルの更新を実施する。
・人事情報管理担当者はイベントに応じてバッチジョブへの入力データを作成している。
現状の問題点として、入力データの作成ミスによって、実際に存在しないコードでテーブルを更新することがあり、給与計算システムでトラブルが起きている。 この問題点を解決するために、RDBMSの参照制約機能を利用することにした。参照制約機能の利用案を表3に示す。
〔参照制約機能の利用の検討〕
参照制約機能を利用する以前には、定期的な組織変更及び人事異動に対応する処理は、図3に示すように “部署” テーブルの行を更新した後、“従業員” テーブルの行を更新していた。
参照制約機能を利用するに当たって、図3中の ①〜⑥の更新手順の変更及び処理時間の検討を行った。
設問1:人事情報管理の業務概要について、(1)、(2)に答えよ。
問題文を見る(1)表1中の(a)〜(c)に入れる適切な字句を答えよ。(b, cは順不同)
模範解答
a:'1'
b:21
c:CURRENT_DATE
解説
解答の導き方
解答(確定):
a:'1'
b:21
c:CURRENT_DATE
a:'1'
b:21
c:CURRENT_DATE
-
aの導出
問題文に「退職フラグを、あらかじめ設定している '0'(在籍)から '1' (退職) に更新する。」とあります。削除対象は「退職した従業員の行」ですから、退職を表す値である '1' を条件に使う必要があります。したがって (a) は '1' です。 -
b, cの導出(目的の整理)
問題文に「毎月25日に、… 当月の20日までに退職した従業員の行を削除する。」とあります。ここで「当月の20日までに」は当月1日から当月20日までを含むことを意味します。 -
DAYELEMENTに関する判断(bの決定)
注記1に「DAYELEMENT関数は、指定された DATE 型の年月日の日の部分を数値として抽出するユーザ定義関数とする。」とあります。表1の述語はDAYELEMENT(退職年月日) < (b) という形ですから、「当月の20日まで」を含めるにはDAYELEMENTが1〜20のとき真になる式にする必要があります。比較が < (b) なので (b) に21を入れてDAYELEMENT(...) < 21とすれば、DAYELEMENT = 20の行も含まれます(例:退職日が当月20日のとき20 < 21は真)。よって (b) = 21です。 -
MONTHELEMENTに関する判断(cの決定)
注記2に「MONTHELEMENT関数は、指定された DATE 型の年月日の年月の部分を数値として抽出するユーザ定義関数とする。」とあります。これは年と月を一体で比較できる値(例えば200701のようなYYYYMM相当)を返すものと解釈して年月単位の大小比較が可能であることを意味します。SQLの比較はMONTHELEMENT(退職年月日) < MONTHELEMENT((c)) になっているため、比較対象には実行時点の年月を与えるのが自然です。SQL実行時の現在日付を表す組込み表現はCURRENT_DATEなので、(c) = CURRENT_DATEが適切です。 -
条件の意味(確認用)
上記を置き換えると、削除条件は次のような意味になります(表示用一行):
DELETE FROM 従業員 WHERE 退職フラグ = '1' AND ((DAYELEMENT(退職年月日) < 21) OR (MONTHELEMENT(退職年月日) < MONTHELEMENT(CURRENT_DATE)))
したがって最終的にa:'1'、b:21、c:CURRENT_DATEが正解です。
誤りやすいポイント
-
(b) に20を入れてしまう誤り
与えられた式がDAYELEMENT(...) < (b) の形なので、当月20日を含めるには (b) = 21とする必要があります。単に「20」と考えると当日(20日)が除外されます。 -
MONTHELEMENTを「月の部分(1〜12)」だけと誤解する誤り
注記2は「年月の部分」を抽出すると書いてあるため、年を含めた年月(例:YYYYMM相当)で比較する想定です。年を含まない単独の月値だと前年・翌年との比較が成立しません。 -
CURRENT_DATEを文字列として 'CURRENT_DATE' としてしまう誤り
CURRENT_DATEは組込みの日付式です。クォートで囲むと文字列扱いになり比較が正しく動作しなくなります。 -
退職フラグの表記を整数1として扱う誤り
問題文はフラグ値を '0'/'1' と示しているため、SQL中では文字リテラル '1' を使う想定です。定義に従ってください。
FAQ
Q: DAYELEMENT(...) <= 20と書けば (b) に20を入れても同じですか?
A: 論理的にはDAYELEMENT(...) <= 20と書けば当月20日を含められますが、問題のSQLでは比較がDAYELEMENT(...) < (b) の形になっています。穴埋めでは与えられた形式を変えずに満たす値を入れる必要があるため、(b) = 21が正解です。実務で式を自由に書けるなら <= 20にするのも問題ありません。
A: 論理的にはDAYELEMENT(...) <= 20と書けば当月20日を含められますが、問題のSQLでは比較がDAYELEMENT(...) < (b) の形になっています。穴埋めでは与えられた形式を変えずに満たす値を入れる必要があるため、(b) = 21が正解です。実務で式を自由に書けるなら <= 20にするのも問題ありません。
Q: MONTHELEMENTが返す具体的な値はどう考えればよいですか?
A: 問題文の注記2は「年月の部分を数値として抽出」とあるため、年と月をまとめた数値(例:200701のようなYYYYMM形式、またはYEAR*100+MONTH)を返すものと解釈します。これにより月単位で正しく大小比較できます。もし実装が月だけ(1〜12)を返す仕様なら、年も別に比較する必要があります。
A: 問題文の注記2は「年月の部分を数値として抽出」とあるため、年と月をまとめた数値(例:200701のようなYYYYMM形式、またはYEAR*100+MONTH)を返すものと解釈します。これにより月単位で正しく大小比較できます。もし実装が月だけ(1〜12)を返す仕様なら、年も別に比較する必要があります。
Q: なぜ日付全体(退職年月日 <= 基準日)で比較しないのですか?
A: 実務では退職年月日 <= 基準日(例:当月の20日)での比較が直接的で分かりやすいです。ただし本問題はDAYELEMENT / MONTHELEMENTを使う構成になっているため、それらの関数の返り値の意味を正しく読み取って穴埋めすることが求められています。
A: 実務では退職年月日 <= 基準日(例:当月の20日)での比較が直接的で分かりやすいです。ただし本問題はDAYELEMENT / MONTHELEMENTを使う構成になっているため、それらの関数の返り値の意味を正しく読み取って穴埋めすることが求められています。
関連キーワード: 参照制約、トリガ、DATE型、ユーザ定義関数、日付比較
設問1:人事情報管理の業務概要について、(1)、(2)に答えよ。
問題文を見る(2)表2中の(d)〜(f)に入れる適切な字句を答えよ。
模範解答
d:従業員.部署コード
e:DISTINCT
f:ORDER BY
解説
解答の導き方
まず問題文の要件を確認します。問題文には "部署コード順に2007年1月1日以降に生まれた扶養対象者をもつ従業員数と扶養対象者数の一覧表が欲しい" とあります。つまり出力列は「部署コード」「従業員数」「扶養対象者数」で、並び順は部署コードの昇順(部署コード順)になります。
SQL2 の骨格を見ると、SELECT に (d) として表示列、COUNT((e) 従業員家族.従業員コード) AS 従業員数、COUNT(*) AS 扶養対象者数 があり、WHERE 句に "従業員.従業員コード = 従業員家族.従業員コード" の結合条件、GROUP BY (d) (f) (d) という形になっています。これを踏まえて (d),(e),(f) を決めます。
- dが何であるか(グループ化・表示する列)
- 要件が「部署コード順に … 一覧表」と明示しているため、(d) は部署コードを表す列です。
- 図1 に従って 部署コード は従業員テーブルの列なので、あいまいさを避けるために完全修飾で 従業員.部署コード を使います。GROUP BY と SELECT の非集約列は一致している必要があるため、(d) = 従業員.部署コード が妥当です。
- eが何であるか(従業員数の集計方法)
- 従業員数は「扶養対象者をもつ従業員の人数」です。従業員家族テーブルは扶養対象者ごとに行があるので、単に COUNT(従業員家族.従業員コード) とすると扶養対象者行の数(扶養対象者数)になってしまい、同一従業員が複数の扶養対象者を持つ場合に過大カウントされます。
- 重複する従業員コードを一意に数えるには DISTINCT を使います。問題のプレースホルダの形が COUNT((e) 従業員家族.従業員コード) なので、(e) = DISTINCT が正しいです。つまり COUNT(DISTINCT 従業員家族.従業員コード) により従業員の数が得られます。
- fが何であるか(並べ替え)
- 要件に「部署コード順に」とあるため、結果を部署コードでソートする必要があります。SQL の並べ替え句は ORDER BY なので、(f) = ORDER BY です。
- 実装上は ORDER BY に SELECT の列名やエイリアス、列番号を使える RDBMS もありますが、問題文の意図に沿ってかつ可搬性を考えると ORDER BY 従業員.部署コード のように明示するのが安全です。
以上から最終的な置き換えは次のとおりです。
d:従業員.部署コード
e:DISTINCT
f:ORDER BY
e:DISTINCT
f:ORDER BY
誤りやすいポイント
- 従業員数と扶養対象者数を取り違える:従業員数は従業員ごとの人数(重複を除く)であり、扶養対象者数は従業員家族テーブルの行数(条件を満たす扶養対象者の個数)です。DISTINCT の有無で結果が大きく変わります。
- COUNT() と COUNT(列) の違い:COUNT() は行数(NULL を含む)を数え、COUNT(列) はその列が NULL でない行だけを数えます。今回の扶養対象者数には COUNT(*) が適しています(WHERE によって該当行のみを抽出しているため)。
- GROUP BY と SELECT の整合性:GROUP BY に指定する式は、SELECT の非集約列と一致させる必要があります。あいまいさを避けるために完全修飾(例:従業員.部署コード)で書くと安全です。
- ORDER BY の書き方:多くの RDBMS は ORDER BY にエイリアスや列番号を許容しますが、問題や移植性を考えると元の列式(従業員.部署コード)を明示するのが確実です。
- 結合の種類:問題の要件は「扶養対象者をもつ従業員」を集計するため内部結合(暗黙の結合 WHERE 句での結合条件)で問題ありませんが、扶養対象者がいない従業員も列挙したい場合は LEFT OUTER JOIN と COALESCE 等の工夫が必要です。
FAQ
Q: COUNT(DISTINCT 従業員家族.従業員コード) と COUNT(DISTINCT 従業員.従業員コード) はどちらを使うべきですか?
A: 結果は同じになります(WHERE で両者が等しい結合条件があるため)。ただし問題のプレースホルダは従業員家族.従業員コード の前に (e) を入れる形式なので、(e) は DISTINCT とするのが設問意図に一致します。
A: 結果は同じになります(WHERE で両者が等しい結合条件があるため)。ただし問題のプレースホルダは従業員家族.従業員コード の前に (e) を入れる形式なので、(e) は DISTINCT とするのが設問意図に一致します。
Q: ORDER BY に SELECT 句のエイリアスを使えますか?
A: 多くの RDBMS はエイリアスや列番号を ORDER BY に許容します。しかし移植性や明確さの観点からは、ここでは ORDER BY 従業員.部署コード のように元の列を明示するのが安全です。
A: 多くの RDBMS はエイリアスや列番号を ORDER BY に許容します。しかし移植性や明確さの観点からは、ここでは ORDER BY 従業員.部署コード のように元の列を明示するのが安全です。
Q: なぜ扶養対象者数に COUNT() を使うのですか?
A: WHERE で扶養対象者(扶養フラグ = '1' かつ 生年月日 >= TO_DATE('2007-01-01'))に絞っているため、その結果行の数が「扶養対象者数」を表します。COUNT() はその行数を正しく返します。
A: WHERE で扶養対象者(扶養フラグ = '1' かつ 生年月日 >= TO_DATE('2007-01-01'))に絞っているため、その結果行の数が「扶養対象者数」を表します。COUNT() はその行数を正しく返します。
関連キーワード: GROUP BY、ORDER BY、DISTINCT、COUNT、結合
(1)次の(a)、(b)の処理を実行した場合、正常終了と、制約検査でエラーのどちらになるか。答案用紙の正常終了・エラーのいずれかを○で囲んで示せ。エラーとなる場合は、その理由を、40字以内で具体的に述べよ。
(a)新規従業員登録のために、所属未定(部署コードがNULL)の行を“従業員”テーブルに挿入する。
(b)ある部署の管理者退職に伴い、“従業員”テーブルから当該従業員を削除する。
模範解答
(a):結果:正常終了
理由:(なし)
(b):結果:エラー
理由:“部署”テーブルの管理者従業員コードの参照制約に違反するから
解説
解答の論理構成
- “参照制約” の仕様
【問題文】では挙動モードに「① NO ACTION」「② CASCADE」が定義されています。NO ACTIONは参照整合性を保持できない操作を禁止します。 - (a) 所属未定の従業員挿入
- 【表3】で “従業員”.“部署コード” の挙動は UPDATE/NO ACTION、DELETE/CASCADE、検査契機は「猶予モード」です。
- 挿入時に “部署コード” がNULLの場合、参照制約は適用されません(参照値が存在しないため)。
- よって制約違反は発生せず「正常終了」と判断できます。
- (b) 管理者従業員の削除
- “部署”.“管理者従業員コード” は【表3】で DELETE/NO ACTION と定義されています。
- 当該従業員が “部署” テーブルから参照されている状態で “従業員” テーブルから削除すると、参照先が失われるためNO ACTIONによりエラーが返されます。
- したがって「エラー」になり、その理由は「“部署”テーブルの管理者従業員コードの参照制約に違反するから」です。
誤りやすいポイント
- NULLでも必ず制約検査が行われると誤解する。
- CASCADEとNO ACTIONを参照元・参照先で混同する。
- 猶予モード と 即時モード の違いを結果判定に結び付けられない。
- “従業員家族” 側の DELETE/CASCADE に気を取られて (b) が許可されると誤判断する。
FAQ
Q: DELETE/CASCADE が設定されている列は削除しても絶対にエラーにならないのですか?
A: いいえ。参照される側(親)の列に DELETE/CASCADE があれば連鎖削除が行われますが、他に DELETE/NO ACTION の制約が存在すればそちらが優先し、エラーになる場合があります。
A: いいえ。参照される側(親)の列に DELETE/CASCADE があれば連鎖削除が行われますが、他に DELETE/NO ACTION の制約が存在すればそちらが優先し、エラーになる場合があります。
Q: 猶予モード の制約違反はトランザクション終了まで検出されませんか?
A: はい。検査はトランザクション終了時です。ただし今回の (a) ではNULLなのでそもそも検査対象外です。
A: はい。検査はトランザクション終了時です。ただし今回の (a) ではNULLなのでそもそも検査対象外です。
関連キーワード: 参照制約、NO ACTION, CASCADE, NULL値、検査契機
(2)“従業員”テーブル及び “従業員家族” テーブルから退職した従業員の行を削除して別テーブルに保存するトリガについて、参照制約を利用することによって不具合が発生する。その対策として、“従業員”テーブルのトリガ定義を変更した上で、新たなトリガを定義する。新たに定義するトリガについて、対象となるテーブルのテーブル名、実行タイミング、処理内容をそれぞれ答えよ。
模範解答
テーブル名:従業員家族
実行タイミング:削除の後
処理内容:削除した行を別テーブルに挿入する。
解説
解答の導き方
-
現状のトリガ処理を確認する
問題文には「実行タイミングを“従業員”テーブルの削除の後としたトリガを定義している。」とあり、その処理として「“従業員家族”テーブルのその従業員の家族の行について、別テーブルに挿入し、その後削除する。」とあります。つまり現在は従業員側の AFTER DELETE トリガ内で、従業員行の退避と同時にその従業員に紐づく従業員家族の行を取得してアーカイブし、続けて削除していることが分かります。 -
表3の参照制約の設定を確認する
表3では従業員家族の従業員コードに対する参照制約の DELETE が CASCADE に設定されています。したがって従業員行を削除すると、RDBMS が従業員家族の該当行を自動的に削除します。 -
なぜ親の AFTER DELETE で家族行をアーカイブしてはいけないかを論理的に導く
トリガの説明に「トリガはテーブルに対する変更操作を契機に、…操作対象の行ごとに実行する」とあるため、従業員家族テーブルに対する削除(明示的削除でも CASCADE による削除でも)は従業員家族側の DELETE トリガの発火対象になります。逆に、親(従業員)の AFTER DELETE トリガ内で子(従業員家族)行を参照してアーカイブしようとすると、CASCADE によって子行が既に削除されている場合があり、子行を取得できずアーカイブできないか、削除操作でエラーになる可能性があります。
また問題文は「実行タイミングを挿入・更新・削除の前として定義したトリガの処理の中で、テーブルに対する変更操作を行うことはできない」と明記しているため、BEFOREトリガ内で別テーブルへ挿入する方式は利用できません。 -
したがって取るべき対策(論理的帰結)
- 子行の削除が発生するタイミング(CASCADEによる削除を含む)で確実にアーカイブ処理が実行されるように、従業員家族テーブル側に削除後トリガを定義します。
- BEFOREトリガは別テーブル挿入が禁止されているため、実行タイミングは削除の後(AFTER)にします。
- 親トリガ(従業員側)の中の「従業員家族を別テーブルに挿入して削除する」処理は削除しておく必要があります(重複アーカイブや参照エラーを避けるため)。
最終的な答え(新たに定義するトリガ)
- テーブル名:従業員家族
- 実行タイミング:削除の後
- 処理内容:削除した行を別テーブルに挿入する。
誤りやすいポイント
- 親(従業員)側の AFTER DELETE に家族行のアーカイブ処理を残したままにする。CASCADE によって子行が既に削除されている可能性があるため失敗または二重処理になる。
- BEFORE DELETE を選ぶと「OLD の値が取れない」と誤解する(実際は OLD は利用可能)が、問題文の制約により BEFORE では別テーブル挿入などの変更操作ができない点を見落とす。
- 「CASCADEだと子側のトリガが起動しない」と誤認する。子テーブルへの削除操作が発生すれば子側のトリガは行ごとに実行される点を理解しておく。
- 親・子双方でアーカイブ処理を残しておき、アーカイブ重複や整合性不整合を招くこと。親側の家族関連処理は削除するか無効化する必要がある。
- 大量削除時の性能問題(行ごとのトリガ実行によりオーバーヘッドが出る)を考慮しないこと。
FAQ
Q: 親側の AFTER DELETE トリガで家族行を取得してアーカイブする方が分かりやすいのではないですか?
A: 表3で従業員家族の DELETE が CASCADE に設定されているため、親の削除操作に伴って子行は自動的に削除されます。したがって親の AFTER DELETE で子行を参照してアーカイブすることは子行が既に存在しない可能性があり信頼できません。子側の DELETE トリガに移すのが確実です。
A: 表3で従業員家族の DELETE が CASCADE に設定されているため、親の削除操作に伴って子行は自動的に削除されます。したがって親の AFTER DELETE で子行を参照してアーカイブすることは子行が既に存在しない可能性があり信頼できません。子側の DELETE トリガに移すのが確実です。
Q: BEFORE DELETE トリガでアーカイブ処理を書けませんか?
A: BEFOREトリガではOLDの値は参照できますが、問題文にある通り「実行タイミングを挿入・更新・削除の前として定義したトリガの処理の中で、テーブルに対する変更操作を行うことはできない」ため、別テーブルへの挿入(アーカイブ)はできません。必ずAFTERを使います。
A: BEFOREトリガではOLDの値は参照できますが、問題文にある通り「実行タイミングを挿入・更新・削除の前として定義したトリガの処理の中で、テーブルに対する変更操作を行うことはできない」ため、別テーブルへの挿入(アーカイブ)はできません。必ずAFTERを使います。
Q: CASCADE による削除でも従業員家族側の AFTER DELETE トリガは確実に起動しますか?
A: トリガは「テーブルに対する変更操作を契機に、…操作対象の行ごとに実行する」と記載されています。CASCADE による子テーブルの削除もテーブルに対する変更操作なので、子側の DELETE トリガは該当行ごとに発火して OLD の値を使ってアーカイブできます。
A: トリガは「テーブルに対する変更操作を契機に、…操作対象の行ごとに実行する」と記載されています。CASCADE による子テーブルの削除もテーブルに対する変更操作なので、子側の DELETE トリガは該当行ごとに発火して OLD の値を使ってアーカイブできます。
関連キーワード: 参照制約、CASCADE、トリガ、実行タイミング、検査契機
設問3:〔参照制約機能の利用の検討〕に示した、参照制約機能を利用した後について、(1)〜(3)に答えよ。
問題文を見る(1)図3中の①〜⑥のデータの削除、挿入、更新の順序を変更せずに運用した場合、不具合が発生することがある。不具合が発生する契機を図3中の丸数字で答えよ。また、発生する不具合の内容を、40字以内で述べよ。
模範解答
契機:①
不具合:削除された部署に所属している従業員が、“従業員”テーブルから削除される。
解説
解答の論理構成
-
前提確認
【表3】に従業員/部署コード … DELETE CASCADEとある。削除元(親)は“部署”、削除先(子)は“従業員”である。 -
参照制約導入後の挙動
①で部署行を削除すると、DELETE‐CASCADE により参照している従業員行が自動削除される。
⑤で部署コードを更新しようとしても、既に該当従業員行はないので処理以前にデータが失われる。 -
結論
よって不具合の契機は①、内容は
「削除された部署に所属している従業員が、“従業員”テーブルから削除される。」
誤りやすいポイント
- 「NO ACTIONはエラーになる」と早合点し、CASCADEの連鎖削除を見落とす。
- ⑤の UPDATE が先に走ると考え、①の影響を評価しない。
- 検査契機モードの「猶予モード」を即時チェックと混同し、トランザクション終了まで削除が遅れると誤解する。
FAQ
Q: ①ですぐにエラーが返らないのですか?
A: DELETE CASCADE なのでエラーではなく、自動的に従業員行が削除されるため、担当者は問題に気付きにくいです。
A: DELETE CASCADE なのでエラーではなく、自動的に従業員行が削除されるため、担当者は問題に気付きにくいです。
Q: 検査契機モードが「猶予モード」ならトランザクション終了まで削除されませんか?
A: “従業員”.“部署コード”の検査契機モードは【猶予モード】ですが、親行削除に伴うCASCADEはトランザクション内で即時に論理削除されます。
A: “従業員”.“部署コード”の検査契機モードは【猶予モード】ですが、親行削除に伴うCASCADEはトランザクション内で即時に論理削除されます。
関連キーワード: 参照制約、CASCADE, 外部キー、トランザクション、データ整合性
設問3:〔参照制約機能の利用の検討〕に示した、参照制約機能を利用した後について、(1)〜(3)に答えよ。
問題文を見る(2)(1)の不具合の回避のために、図3中の①、③、⑤の順序を変更する。どのように変更すればよいか、①、③、⑤の変更後の順序を答えよ。
模範解答
[ ③ ] → ② → [ ⑤ ] → ④ → [ ① ] → ⑥
解説
解答の導き方
- 問題文から参照制約とトリガに関する重要な事実を抽出する
- 表3を見ると、従業員の「部署コード」に対する参照制約の「DELETE」の挙動モードが「CASCADE」、検査契機モードが「猶予モード」となっています。つまり「DELETE」によって参照先の部署行を削除すると、参照元の従業員行は参照制約の定義に従って自動的に処理される可能性があること、参照整合性の検査はトランザクション終了時に行われることがわかります。
- また、問題文はトリガについて「削除した ‘従業員’ テーブルの行を別テーブルに挿入する」「‘従業員家族’ テーブルのその従業員の家族の行について、別テーブルに挿入し、その後削除する」と記載しています。削除によって従業員行が除去されると、その削除に伴うトリガ処理(アーカイブや家族行の削除)が実行される点も重要です。
- 元の順序(①→②→③→④→⑤→⑥)だと何が起きるかを考える
- ①で部署を削除してしまうと、表3の指定により「DELETE=CASCADE」が効いている場合、当該部署に所属する従業員行が自動的に削除されます。従業員の削除に対してはトリガが定義されており、そのトリガで従業員行や従業員家族の行が別テーブルへ移されてから削除されます。すなわち、従業員の所属替えを行うはずが、従業員データが意図せず削除・アーカイブされてしまう危険があります。
- さらに、表3の「検査契機モード」が「猶予モード」であるため、参照整合性の検査はコミット時に行われます。途中でコミットしてしまうと、その時点で整合性違反(あるいは意図したCASCADEの副作用)が確定してしまう可能性があります。
- 安全な順序の論理(なぜ ③ → ⑤ → ① が良いか)
- まず ③(部署の新規挿入)を行えば、従業員を移す先の部署コードが確実に存在します。
- 次に ⑤(従業員の部署コード更新)を行えば、従業員は旧部署から新部署へ移動し、旧部署を参照する行がなくなります。表3の参照検査が「猶予モード」なので、挿入と更新を同一トランザクション内で行い最後にコミットすれば、最終状態で参照整合性が保たれます。
- 最後に ①(旧部署の削除)を行えば、従業員が既に新部署に移っているため、DELETE の「CASCADE」によって従業員が削除される事態は避けられます。また、従業員削除に伴うトリガの誤動作(不要なアーカイブや家族データの削除)も防げます。
以上から、同じ処理群(部署の切替)は「新部署を作る → 従業員を移す → 旧部署を消す」という順序、すなわち ③ → ⑤ → ① が安全であり正解です。
補足(コミットの扱い)
- 実際のバッチではコミット位置が運用に依存しますが、参照制約の「猶予モード」を利用するなら、可能であれば ③ と ⑤ と ① を同一トランザクションで実行してからコミットするのが最も整合性を保ちやすいです。モデル解答の全体表現は [ ③ ] → ② → [ ⑤ ] → ④ → [ ① ] → ⑥ のようにコミットが挟まれる形を示していますが、問題の問われ方(①、③、⑤の順序)に対する本質は ③ → ⑤ → ① です。
誤りやすいポイント
- 「CASCADEは親を削除しても参照元(子)が残るから即座に違反になる」と誤解する点。実際は「CASCADE」は子行を自動削除して参照不整合を残さない動作をするが、その削除がトリガや他の制約(即時モードの外部キー)と干渉して問題になることがある点を忘れやすいです。
- 「猶予モード」と「即時モード」を混同する点。猶予モードはトランザクション終了時に検査するため、トランザクション内で一時的に不整合を許すが、即時モードは各SQL実行後に検査されるため順序を厳密に守る必要があります。
- 外部キーの向き(参照先/参照元)を取り違える点。どちらが親テーブルかを誤認すると、削除や更新の影響を逆に考えてしまいます。
- トリガの影響を見落とす点。削除によるトリガが別テーブルへのアーカイブや家族行の削除を行うなら、CASCADEによる子削除は実務上大きな副作用を生みます。
- コミットの位置を軽視する点。特に「猶予モード」ではコミット時に整合性が確定するため、途中でコミットすると意図しない確定(エラーや不要な削除)につながります。
FAQ
Q: なぜ「挿入→更新→削除(③→⑤→①)」にまとめて実行する方が安全なのですか?
A: 表3の従業員.部署コードの参照制約は「DELETE に対して『CASCADE』」かつ「検査契機モードが『猶予モード』」であり、挿入と更新を同一トランザクションで行って最終状態が整合であればコミット時に問題になりません。先に旧部署を削除すると「CASCADE」で従業員が削除され、削除トリガが発動して不要なアーカイブや家族データの削除が発生するため、移行先を先に用意してから従業員を移し、その後旧部署を削除するのが安全です。
A: 表3の従業員.部署コードの参照制約は「DELETE に対して『CASCADE』」かつ「検査契機モードが『猶予モード』」であり、挿入と更新を同一トランザクションで行って最終状態が整合であればコミット時に問題になりません。先に旧部署を削除すると「CASCADE」で従業員が削除され、削除トリガが発動して不要なアーカイブや家族データの削除が発生するため、移行先を先に用意してから従業員を移し、その後旧部署を削除するのが安全です。
Q: コミットはどこで行うのが実務的に望ましいですか?
A: 技術的には ③→⑤→① を同一トランザクションで行い最後にコミットするのが整合性確保の観点で最も確実です。ただし、運用上の理由(長時間トランザクションを避ける等)で分割する場合は、必ず「新部署の挿入」を更新より前にし、更新後に旧部署削除が確実に従業員に影響しないことを確認してから削除側をコミットしてください。即時モードの制約がある場合は、各ステップ直後に制約チェックが走るので注意が必要です。
A: 技術的には ③→⑤→① を同一トランザクションで行い最後にコミットするのが整合性確保の観点で最も確実です。ただし、運用上の理由(長時間トランザクションを避ける等)で分割する場合は、必ず「新部署の挿入」を更新より前にし、更新後に旧部署削除が確実に従業員に影響しないことを確認してから削除側をコミットしてください。即時モードの制約がある場合は、各ステップ直後に制約チェックが走るので注意が必要です。
Q: ON DELETE CASCADE で削除された行に対して定義した AFTER DELETE トリガは実行されますか?
A: はい。カスケードで削除された行も通常の削除と同様に行削除が発生しているため、AFTER DELETE トリガは発生します。したがってカスケードによる削除はトリガの副作用(アーカイブや関連行の削除)を引き起こします。
A: はい。カスケードで削除された行も通常の削除と同様に行削除が発生しているため、AFTER DELETE トリガは発生します。したがってカスケードによる削除はトリガの副作用(アーカイブや関連行の削除)を引き起こします。
関連キーワード: 参照制約、外部キー、カスケード削除、猶予モード、即時モード、トリガ
設問3:〔参照制約機能の利用の検討〕に示した、参照制約機能を利用した後について、(1)〜(3)に答えよ。
問題文を見る(3)表3に示すとおり、“従業員”テーブルの部署コードに参照制約が猶予モードで設定されている。この状況で、“部署”テーブルの部署コードを更新したときの振る舞いに関して、(a)、(b)に答えよ。
(a)RDBMSは猶予モードの制約の検査のために、トランザクション終了時にどのような検査を行っているか、検査内容を55字以内で具体的に述べよ。
(b)(a)の検査を行う際、想定よりも処理時間が長くなるおそれがある。その理由を50字以内で具体的に述べよ。
模範解答
(a):更新によって無くなった部署コードが、“従業員”テーブルの“部署コード”に存在しないことを確認する。
(b):“従業員”テーブルの“部署コード”に索引がなく、全行を参照しなければならないから
解説
解答の論理構成
- 参照制約の仕様確認
【問題文】「① 即時モード」「② 猶予モード: トランザクション終了時に…制約を検査する。」
よって猶予モードでは処理完了後にまとめて検査。 - 検査内容の特定
・表3で “従業員”.“部署コード” が “部署”.“部署コード” を参照。
・更新により “部署” テーブルから削除・変更されたコードが “従業員” に残っていれば参照整合性違反。
⇒ 検査は「更新でなくなった部署コードが、参照元に存在しないこと」の確認となる。 - 処理時間が延びる理由
・図1の注記で「索引は、主キーだけに定義されている。」
・“部署コード” は主キーでないため索引が無い。
・検査時には “従業員” の全行を検索(フルスキャン)。行数が多いほど時間増大。
誤りやすいポイント
- 猶予モードを「最後のSQL実行直後」と誤解し、途中で検査が行われると考えてしまう。
- CASCADE動作と猶予モード検査を混同し、検査が不要になると思い込む。
- 索引が主キーにのみ定義されている事実を見落とし、処理時間の影響源を誤答。
FAQ
Q: CASCADEが指定されているのに検査が必要なのはなぜですか?
A: CASCADEが働くのは参照先行の削除・更新時に連鎖操作を行う場面です。猶予モードでは連鎖後に整合性が保たれているかを改めて確認します。
A: CASCADEが働くのは参照先行の削除・更新時に連鎖操作を行う場面です。猶予モードでは連鎖後に整合性が保たれているかを改めて確認します。
Q: 即時モードと猶予モードの混在は性能に影響しますか?
A: 即時モードはSQL実行ごとに検査するため応答時間が延びやすく、猶予モードはトランザクション終了まで処理を遅延させるのでロールバック時のペナルティが大きくなります。
A: 即時モードはSQL実行ごとに検査するため応答時間が延びやすく、猶予モードはトランザクション終了まで処理を遅延させるのでロールバック時のペナルティが大きくなります。
Q: 検査時間を短縮する方法は?
A: “従業員”.“部署コード” に索引を追加すればフルスキャンを避けられ、猶予モード検査も高速化します。
A: “従業員”.“部署コード” に索引を追加すればフルスキャンを避けられ、猶予モード検査も高速化します。
関連キーワード: 参照制約、猶予モード、フルテーブルスキャン、索引、CASCADE







