なぜ正規化が必要か
正規化とは、更新時異常 (挿入異常・更新異常・削除異常) を排除するために、リレーションを関数従属性の分析に基づいて分割し、一つの事実を一つの場所にのみ格納するデータベース設計手法である。
同じ情報を何度も書くと何が困るのか
こんな表を想像してほしい。従業員 1 人につき 1 行、「氏名 / 部署ID / 部署名 / 部署所在地」を書くタイプの表。 同じ部署に何人か所属していると、その 部署の名前や所在地が人数分だけ何度も書かれる ことになる。
| 社員ID | 氏名 | 部署ID | 部署名 | 部署所在地 |
|---|---|---|---|---|
| E001 | 山田 | D01 | 営業部 | 東京 |
| E002 | 田中 | D01 | 営業部 | 東京 |
| E003 | 佐藤 | D02 | 開発部 | 大阪 |
| E004 | 鈴木 | D02 | 開発部 | 大阪 |
この「同じことを何度も書いている」状態は、ぱっと見は問題なさそうでも、 いざデータを追加 / 変更 / 削除しようとすると 3 種類の困りごとを引き起こす。 これらをまとめて 更新時異常 (update anomalies) と呼ぶ。
- 挿入異常 — 追加したいのに追加できない
- 更新異常 — 一部だけ書き換えて矛盾が起きる
- 削除異常 — 消したい情報と一緒に、消したくない情報も消える
まずは各異常が「実際どんな場面で起きるか」を見てみる。 以下 3 つの図はコマ送りで進められるので、「次へ」を押しながら順に見てほしい。
1. 挿入異常 — 追加したいのに追加できない
| 社員ID | 氏名 | 部署ID | 部署名 | 部署所在地 |
|---|---|---|---|---|
| E001 | 山田 | D01 | 営業部 | 東京 |
| E002 | 田中 | D01 | 営業部 | 東京 |
| E003 | 佐藤 | D02 | 開発部 | 大阪 |
| E004 | 鈴木 | D02 | 開発部 | 大阪 |
| ? | ? | ? | ? | ? |
初期状態: D03 (人事部) はまだ存在しない
「D03 = 人事部 (東京)」を登録したくても、 このテーブルでは 社員行としてしか事実を書けない。 社員が居ないと社員ID を NULL にせざるを得ず、主キー制約に引っかかって登録できない。 「部署」という事実の格納先がそもそも独立して存在していないことが根本原因。
2. 更新異常 — 一部だけ書き換えて矛盾が起きる
| 社員ID | 氏名 | 部署ID | 部署名 | 部署所在地 |
|---|---|---|---|---|
| E001 | 山田 | D01 | 営業部 | 東京 |
| E002 | 田中 | D01 | 営業部 | 東京 |
| E003 | 佐藤 | D02 | 開発部 | 大阪 |
| E004 | 鈴木 | D02 | 開発部 | 大阪 |
初期状態: D01 の 2 行はどちらも「営業部」
「D01 = 営業部」という 1 つの事実が、社員の数だけ複製されている。 改称のためには D01 を持つ 全行 を漏れなく更新する必要があり、 1 行でも忘れると同じ D01 が「営業部」と「セールス部」の 2 つの部署名を持つ矛盾状態になる。 「部署」の事実が独立していれば 1 行の UPDATE で済むはずのもの。
3. 削除異常 — 消したくない情報も一緒に消える
| 社員ID | 氏名 | 部署ID | 部署名 | 部署所在地 |
|---|---|---|---|---|
| E001 | 山田 | D01 | 営業部 | 東京 |
| E002 | 田中 | D01 | 営業部 | 東京 |
| E003 | 佐藤 | D02 | 開発部 | 大阪 |
| E004 | 鈴木 | D02 | 開発部 | 大阪 |
初期状態: D02 (開発部/大阪) には 佐藤 (E003)・鈴木 (E004) の 2 名
「D02 = 開発部 (大阪)」という 部署そのものの事実 は、 このテーブルでは「社員行」の中にしか存在していない。 最後の所属者を消した瞬間、部署情報も一緒に失われる。 後で新しい社員を D02 に配属したくても、部署名や所在地はもう分からない。
解決の指針: 「同じ情報は 1 か所にだけ書く」
3 つの異常はいずれも 同じ情報を複数の場所に書いている ことが原因。 逆に言えば、「同じ情報は 1 か所にだけ書く」 と決めれば、これらは自然に解消する。 これが正規化の一番大事な指針で、教科書では「一事実一箇所」(One Fact in One Place) と呼ばれることも多い。
具体的にはどうするか。 「部署」の情報 (部署ID・部署名・所在地) は本来「部署ごとに 1 つ」の情報なので、部署テーブルを別に作って部署の情報はそこにだけ書く。 従業員テーブルには「どの部署か」を示す 部署ID だけを持たせて、 部署の詳細を知りたい時は部署テーブルを見に行けばいい。
| 社員IDPK | 氏名 | 部署ID |
|---|---|---|
| E001 | 山田 | D01 |
| E002 | 田中 | D01 |
| E003 | 佐藤 | D02 |
| E004 | 鈴木 | D02 |
| 部署IDPK | 部署名 | 部署所在地 |
|---|---|---|
| D01 | 営業部 | 東京 |
| D02 | 開発部 | 大阪 |
部署の名前も所在地も「D01 は営業部で東京」「D02 は開発部で大阪」と、それぞれ 1 か所にしか書かれていない。 従業員テーブルには 部署ID という参照だけが残る。
こうすると:
- 更新異常が消える: 部署名を変える時は部署テーブルの 1 行を書き換えるだけで全従業員の表示に反映される。「一部だけ書き換わって矛盾」が起きようがない。
- 挿入異常が消える: 従業員が 1 人もいない新部署も、部署テーブルに 1 行足せば登録できる。
- 削除異常が消える: 従業員が全員辞めても、部署の情報は部署テーブルに残っているので消えない。
正規化はこの指針を「機械的に」進める手順
「同じ情報を 1 か所に書く」と言われても、実際どうテーブルを分ければいいか迷う。 そこで先人たちは、「どの列がどの列を決めているか」という関係 (関数従属性) を見れば機械的に分割できる、という手順を作った。 これが 第1正規形・第2正規形・第3正規形と呼ばれる正規化の各ステップ。
実務では 第3正規形まで 進めれば十分とされることが多い。 それより先 (BCNF・4NF・5NF) は現実の業務モデルでは滅多に問題にならないため、まずは 3NF を目標にすればいい。 逆に、性能上の理由で「わざと同じ情報を書いておく」判断もあり、これを非正規化 と呼ぶ。
よくある疑問
関連トピック
- 基礎関数従属性
属性 X の値が決まると属性 Y の値も一意に決まる関係を関数従属という。この概念は 1NF〜3NF、BCNF まで正規化の全ステップの判定基準になる。部分関数従属・推移関数従属など派生概念も整理する。
- 基礎キーの階層
正規化の議論に入る前に、スーパーキー・候補キー・主キーの3階層と、外部キー・代替キー・複合キーとの関係を整理する。2NF/3NF の「非キー属性」「部分従属」を語るための語彙を揃える。
- 基礎第1正規形
全ての属性がアトミック値をとり、繰り返しグループを含まない状態が第1正規形。非1NF (unnormalized form) の代表例を並置し、1NF に変換する具体的手続きを図解する。
もっと学びたい方へ(おすすめ書籍)
テーブル設計と正規化、パフォーマンス考慮のインデックス設計まで実務レベルで学べる定番書。第2版ではクラウド対応も強化。
リレーショナルモデルの理論から、インデックス設計を含む実務で使えるSQLまで解説。
SQLの本質的な使い方と、インデックスが効くクエリの書き方を学べる。ウィンドウ関数など現代SQLも網羅。
実務でやりがちなSQL・DB設計のアンチパターンとその回避策を体系的に学べる。
PostgreSQLの内部構造・ストレージ・インデックス機構を丁寧に解説。設計と運用計画の鉄則が学べる。
IPAデータベーススペシャリスト試験の総合対策書。インデックス関連は本サイトと合わせて学ぶと理解が深まる。
本セクションはAmazonアソシエイトのリンクを含みます。
もっと深くDBを学びたい方へ。
たいてっくが、SQL・データベース設計・パフォーマンスチューニング・ IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。