データベーススペシャリスト 2010年 午前2 問09
問題文
次の表を、第3正規形まで正規化を行った場合、幾つの表に分割されるか。ここで、顧客の1回の注文に対して1枚の受注伝票が作られ、顧客は1回の注文で一つ以上の商品を注文できるものとする。

選択肢
ア:2
イ:3
ウ:4(正解)
エ:5
🔒 解説は解答すると表示されます
第3正規形への分割【午前2解説】
正解の理由
与えられた表は行単位で「受注伝票の明細」を表しており、候補キーは複合キー 受注番号, 商品コード です。ここから関数従属を整理すると、受注番号 → 顧客コード、受注日、顧客コード → 顧客名、商品コード → 商品名、単価、そして受注数と受注金額は明細側に属します。顧客名が受注番号に直接従属しているわけではなく、受注番号 → 顧客コード → 顧客名という「推移的従属」が存在します。推移的従属を除去して第3正規形にするには顧客情報と注文情報を分離し、さらに商品情報も独立させる必要があります。したがって最終的に分割される表は「受注ヘッダ」「顧客」「商品」「受注明細」の4表となり、選択肢ウが正しい理由です。
解法ステップ
- 元表の主キー候補を確認
- 行が「1受注で複数商品」を表すため、明細レベルの主キーは複合キー 受注番号, 商品コード。
- 関数従属を抽出
- 受注番号 → 顧客コード, 受注日
- 顧客コード → 顧客名(推移的従属)
- 商品コード → 商品名, 単価
- (受注数 → 受注金額 は算出可能だが現実データでは保存する場合もある)
- 1NF→2NF:部分従属の除去
- 受注番号にのみ依存する属性(顧客コード、受注日)は受注のヘッダへ分離
- 商品にのみ依存する属性(商品名、単価)は商品テーブルへ分離
- 2NF→3NF:推移的従属の除去
- 顧客名は顧客コードに依存するため顧客テーブルを作成して分離
- 結果
- 受注ヘッダ(受注番号, 顧客コード, 受注日)
- 顧客(顧客コード, 顧客名)
- 商品(商品コード, 商品名, 単価)
- 受注明細(受注番号, 商品コード, 受注数, 受注金額)
合計 4 表 → ウ
選択肢別の誤答解説
- ア: 2
- 受注ヘッダと明細の2表だけにすると、商品情報(商品名・単価)や顧客名が繰返し出現して冗長・更新異常が残ります。推移的従属も解消できないため第3正規形になりません。
- イ: 3
- 3表だと顧客情報か商品情報のどちらかが分離されないまま残り、推移的従属や商品の部分従属のどちらかが除去されません。例えば顧客名を分離しても商品属性を分離しないと部分従属が残ります。
- ウ: 4
- 正しい。受注ヘッダ・受注明細・顧客・商品に分けることで、部分従属と推移的従属を適切に除去し第3正規形に到達します。
- エ: 5
- 不必要に細かく分割した場合(例えば受注金額を別表にするなど)、設計として過度な分割で結合コストが増え、通常の第3正規形の要求を超えます。第3正規形では推移的従属や部分従属を除去すればよく、5表に分ける必然性はありません。
よくある誤解
- 「顧客名は受注番号に直接従属する」と考える誤り
- 実際には受注番号→顧客コード→顧客名 の推移的従属であり、顧客名は顧客コードに従属します。これを見落とすと正しい分割ができません。
- 受注金額や単価は常に商品テーブルに置くべき、という短絡
- 単価は商品属性だが、実務では注文時点の価格を履歴として明細にスナップショット保存することがあり得ます(設計上は「履歴」扱いで正規化の議論と区別する必要あり)。
- 第3正規形=最大分割 と誤解すること
- 第3正規形は必要な関数従属の除去を目的とするもので、無意味な過分割(冗長な分割)は含みません。
補足コラム
典型的な正規化後のスキーマ例(示意)
- 受注ヘッダ: 受注番号 (PK), 顧客コード (FK), 受注日
- 顧客: 顧客コード (PK), 顧客名
- 商品: 商品コード (PK), 商品名, 単価
- 受注明細: 受注番号 (PKの一部, FK), 商品コード (PKの一部, FK), 受注数, 受注金額
実務上のポイント
- 受注金額は単価×受注数で算出可能な派生属性です。第3正規形の観点では保存は不要ですが、価格変動を履歴として残したい場合は明細に単価をコピー保存する(スナップショット)設計が一般的です。正規化と履歴要件は用途に応じてバランスを取ります。
簡単なDDL例(参考)
CREATE TABLE 顧客 (
顧客コード CHAR(6) PRIMARY KEY,
顧客名 VARCHAR(100) NOT NULL
);
CREATE TABLE 受注ヘッダ (
受注番号 INT PRIMARY KEY,
顧客コード CHAR(6) REFERENCES 顧客(顧客コード),
受注日 DATE NOT NULL
);
CREATE TABLE 商品 (
商品コード CHAR(4) PRIMARY KEY,
商品名 VARCHAR(100),
単価 INT
);
CREATE TABLE 受注明細 (
受注番号 INT REFERENCES 受注ヘッダ(受注番号),
商品コード CHAR(4) REFERENCES 商品(商品コード),
受注数 INT,
受注金額 INT,
PRIMARY KEY(受注番号, 商品コード)
);
FAQ
Q: 受注金額は第3正規形では保存してはいけないですか?
A: 第3正規形の理論上は派生属性なので保存は不要ですが、注文時の単価や計算誤差を保持するために明細に保存する設計も実務では一般的です。正規化の目的(冗長排除)と履歴要件を両立させて判断してください。
A: 第3正規形の理論上は派生属性なので保存は不要ですが、注文時の単価や計算誤差を保持するために明細に保存する設計も実務では一般的です。正規化の目的(冗長排除)と履歴要件を両立させて判断してください。
Q: 顧客名を受注表に残しても問題ないですか?
A: 残すと更新異常(顧客名変更時に複数行更新が必要)や冗長が発生します。第3正規形にするなら顧客名は顧客テーブルに移すべきです。
A: 残すと更新異常(顧客名変更時に複数行更新が必要)や冗長が発生します。第3正規形にするなら顧客名は顧客テーブルに移すべきです。
Q: 単価は商品テーブルか明細かどちらに置くべき?
A: 商品の標準単価なら商品テーブル、注文時の実際売価を保持したいなら明細にスナップショットとして保存します。用途により使い分けます。
A: 商品の標準単価なら商品テーブル、注文時の実際売価を保持したいなら明細にスナップショットとして保存します。用途により使い分けます。
関連キーワード: 正規化, 第3正規形, 関数従属, 推移的従属, 受注明細, スキーマ設計, 正規化手順

\ せっかくなら /
データベーススペシャリストを
クイズ形式で学習しませんか?
クイズ画面へ遷移する→
すぐに利用可能!

