問題
次のような繰返し構造をもったレコードからなるデータを、第3正規形に正規化したものはどれか。ここで、下線部分は主キーを表す。また、単位と単価は商品コードごとに決まるものとする。
元のレコード構造:
伝票番号(PK)、日付、顧客コード、顧客名、住所、【繰返し:商品コード、単位、数量、単価】
出典:応用情報技術者試験 平成18年春期 午前問62
※この記事では、主キーを (PK) と表記します。
結論
正解は 「イ」 です。
第3正規形となっているのは以下の構成です。
-
伝票:
伝票番号(PK), 日付, 顧客コード -
顧客:
顧客コード(PK), 顧客名, 住所 -
明細:
伝票番号(PK),商品コード(PK), 数量 -
商品:
商品コード(PK), 単位, 単価
「1つの事実は1箇所にだけ書く」、伝票の発行日は親である伝票テーブルにのみ記載して、明細側は「伝票番号と商品コード」だけで管理する。
これにより、どんなにデータが増えても矛盾が起きないDB設計になります。
解説
❌ 選択肢「エ」がNGな理由:
部分関数従属が残っているため(第1正規形止まり)
-
伝票:
伝票番号(PK), 日付, 顧客コード, 顧客名, 住所 -
明細:
伝票番号(PK),商品コード(PK), 単位, 数量, 単価
パッと見は綺麗ですが、明細テーブルに問題があります。
明細テーブルの主キーは「伝票番号 + 商品コード」の複合キーです。
しかし、問題文に「単位と単価は商品コードごとに決まる」とある通り、
この2つは伝票番号には無関係です。
この状態(部分関数従属)を放置すると、「商品の単価が変わった時、過去の明細データまで全部書き直すか、矛盾が発生する」というデータ異常が起きるのでNG。
❌ 選択肢「ウ」がNGな理由:
推移的関数従属が残っているため(第2正規形止まり)
-
伝票:
伝票番号(PK), 日付, 顧客コード, 顧客名, 住所 -
明細:
伝票番号(PK),商品コード(PK), 数量 -
商品:
商品コード(PK), 単位, 単価
伝票テーブルの中にある「顧客名」と「住所」は、伝票番号に直接従属しているのではなく、「顧客コード」が分かれば決まる項目です。
この状態(推移的関数従属)を放置すると、「まだ一度も伝票を発行していない新規顧客の、名前と住所だけを登録しておくことができない」という不都合が生じるためNG。
❌ 選択肢「ア」がNGな理由:
主キーの制約がガバガバになるため(致命的な罠)
-
伝票:
伝票番号(PK), 顧客コード -
顧客:
顧客コード(PK), 顧客名, 住所 -
明細:
伝票番号(PK),日付(PK),商品コード(PK), 数量 -
商品:
商品コード(PK), 単位, 単価
一見すると全ての項目が分散されて綺麗に見えますが、
明細テーブルの複合主キーに「日付」を追加してしまっている点が最大のバグです。
主キーが担う「重複を許さない(一意性制約)」というルールは、
構成する項目が増えるほど条件がゆるくなります。
もし { 伝票番号, 日付, 商品コード } を主キーにしてしまうと、
システム上は以下のようなデータ登録を許してしまいます。
{ 伝票001, 6月21日, りんご }{ 伝票001, 6月22日, みかん }
現実の業務において、「1枚の伝票(レシート)の中に、違う日付の買い物が混ざる」ことはあり得ないので、この設計はNGです。
まとめ
正規化の選択肢で迷ったら、「そのテーブル構成でシステムを作った時、どんなあり得ないデータが登録できてしまうか?」という事実ベースの極端なテストケース(例:同じ伝票で別の日付を入れる等)を想像すると、正解を導き出しやすいです。
