データベースの正規化をやさしく図解 — 第1・第2・第3正規形とは
「第1正規形? 第3正規形? 何を基準に分けるの?」——正規化は、1つの事実は1か所にだけ持つように表を整理していく作業です。この記事では、ぐちゃぐちゃの表(非正規形)を、第1→第2→第3正規形へと実際の表で段階的に分解しながら、なぜそう分けるのかを解説します。
正規化とは — 「更新時異常」を防ぐ整理術
1つの表に情報を詰め込みすぎると、こんな困りごとが起きます。これを更新時異常(anomaly)と呼びます。
| 種類 | どんな問題か |
|---|---|
| 更新時異常 | 同じ事実が何行にも重複して書かれ、1か所だけ直すと他と矛盾する(例:商品名を1行だけ変更してしまう) |
| 挿入時異常 | 関係ない情報が埋まらないと、新しい行を追加できない(例:まだ注文のない新商品を登録できない) |
| 削除時異常 | ある行を消すと、道連れで別の必要な情報まで消えてしまう |
正規化は、表を適切に分割してこれらの異常が起きない形にする設計手法です。合言葉は「1つの事実は1か所だけ(One fact in one place)」。
出発点:非正規形(繰り返しがある表)
次の「注文票」を例にします。1件の注文に複数の商品がぶら下がっていて、1マスに複数の値(繰り返し項目)が入っています。この状態が非正規形です。
| 注文番号 | 注文日 | 顧客名 | 商品明細(商品番号・商品名・単価・数量) |
|---|---|---|---|
| 1001 | 7/1 | 山田 | P01 りんご 100 ×3 / P02 みかん 80 ×5 |
| 1002 | 7/2 | 佐藤 | P01 りんご 100 ×2 |
第1正規形(1NF)— 繰り返しをなくし「1マス1値」に
ルール:繰り返し項目をなくし、すべてのマスを単一の値にする。 商品明細を行に展開して、1行1明細にします。
| 注文番号 | 注文日 | 顧客名 | 商品番号 | 商品名 | 単価 | 数量 |
|---|---|---|---|---|---|---|
| 1001 | 7/1 | 山田 | P01 | りんご | 100 | 3 |
| 1001 | 7/1 | 山田 | P02 | みかん | 80 | 5 |
| 1002 | 7/2 | 佐藤 | P01 | りんご | 100 | 2 |
これで「1マス1値」になりました。主キーは {注文番号, 商品番号} の組(複合主キー)です。ただし、注文日・顧客名・商品名などが何度も重複しているのが気になります。ここから先の正規化で解消します。
関数従属を見抜く — 何が何を決めるか
次のステップの前に、関数従属を押さえます。「Aが決まればBが1つに決まる」関係を A → B と書き、「BはAに関数従属する」といいます。上の表の関数従属を書き出すと:
第2正規形(2NF)— 部分関数従属を分離
ルール:複合主キーの「一部」だけで決まる項目(部分関数従属)を、別の表に分ける。
「注文日・顧客名」は注文番号だけで決まり、「商品名・単価」は商品番号だけで決まります。これらを切り出して3つの表に分割します。
注文表(主キー:注文番号)
| 注文番号 | 注文日 | 顧客名 |
|---|---|---|
| 1001 | 7/1 | 山田 |
| 1002 | 7/2 | 佐藤 |
商品表(主キー:商品番号)
| 商品番号 | 商品名 | 単価 |
|---|---|---|
| P01 | りんご | 100 |
| P02 | みかん | 80 |
注文明細表(主キー:{注文番号, 商品番号})
| 注文番号 | 商品番号 | 数量 |
|---|---|---|
| 1001 | P01 | 3 |
| 1001 | P02 | 5 |
| 1002 | P01 | 2 |
これで「りんごの単価」は商品表に1か所だけになりました。値上げのときも1行直すだけで済みます(更新時異常の解消)。
第3正規形(3NF)— 推移的関数従属を分離
ルール:主キー以外の項目を経由して決まる項目(推移的関数従属)を、別の表に分ける。
例を少し広げます。もし注文表が「顧客番号・顧客名・顧客住所」を持っていたら、注文番号 → 顧客番号 → 顧客住所 という間接的な(推移的な)従属が生じます。顧客住所は注文ではなく顧客の属性なので、顧客表に分けます。
顧客住所を顧客表へ移せば、住所変更のときも顧客表を1行直すだけ。第3正規形では「主キー以外の項目に依存する項目」がなくなるのがゴールです。基本情報の実務レベルでは、多くの場合この第3正規形までを目指します。
正規化のデメリットと「非正規化」
正規化を進めると表が細かく分かれるため、必要な情報を1画面に集めるにはテーブルの結合(JOIN)が増え、検索が遅くなることがあります。そこで、参照が非常に多く性能が重要な場面では、あえて重複を許して結合を減らす「非正規化」を行うこともあります。正規化は「整合性」、非正規化は「速度」——どちらを優先するかの設計判断です。まず正しく正規化し、必要な箇所だけ意図的に崩す、が定石です。
基本情報技術者試験ではこう出る
「与えられた表は第何正規形か」「第2正規形にするために分割せよ」「関数従属図から主キーを求めよ」といった出題が定番です。押さえるべきは、1NF=繰り返しをなくす/2NF=部分関数従属を除く/3NF=推移的関数従属を除く の3段階と、関数従属(A→B)の読み方。「更新時異常を防ぐために正規化する」という目的もよく問われます。SQLのJOINやキーの話は、この正規化とセットで理解すると腹落ちします。
よくある質問
Q. 主キーと候補キー・外部キーの違いは?
A. 候補キーは「行を一意に識別できる項目(の組)」の候補。その中から代表に選んだものが主キーです。外部キーは、別の表の主キーを参照して表どうしをつなぐ項目(注文明細表の商品番号が、商品表の商品番号を指す、など)。正規化で分けた表は、この外部キーで関連づけます。
Q. ボイス・コッド正規形(BCNF)や第4正規形は覚えるべき?
A. 基本情報レベルでは、まず第3正規形までを確実に理解すれば十分です。BCNF以降は応用情報や実務でより厳密さが必要になったときに学べば大丈夫です。
Q. どこまで正規化すればいい?
A. 実務では第3正規形が一つの目安です。そのうえで、性能要件が厳しい箇所だけ非正規化を検討します。「まず3NF、必要なら崩す」と覚えておくとよいです。
まとめ
正規化は、1つの事実を1か所だけに持たせて更新時異常を防ぐ設計です。1NF=1マス1値、2NF=部分関数従属を除く、3NF=推移的関数従属を除く——この3段階を、関数従属(何が何を決めるか)を手がかりに進めます。手順を表で追えるようになれば、試験の分割問題は確実に解けます。
関連ページ: 基本情報 科目Bの解き方 / 記事一覧