データベース正規化は、ひと言でこうまとめられます: 「同じ事実はただ1か所にだけ保存する。」 これを体系的に行う方法が 1NF → 2NF → 3NF です。

なぜ重要かというと — 同じ情報が複数の行に散らばっていると、1つだけ直して残りを直し忘れた瞬間にデータが矛盾してしまいます。これを 更新異常 といいます。下で実際に体験してみてください。

更新異常 — 体験してみよう

Prof.AがRoom 502に引っ越しました。セルをクリックして更新してください。非正規テーブルでは全行を変更する必要があります。

Before(非正規)
studentcourseinstructorofficegrade
S1 KimMATH101Prof.ARoom 301 ← clickA
S1 KimCS102Prof.BRoom 405B+
S2 LeeCS102Prof.BRoom 405A
S3 ParkMATH101Prof.ARoom 301 ← clickB
0/2 更新済み
After(3NF)
Instructorsinstructor_idinstructor_office
Prof.ARoom 502
Prof.BRoom 405
3NFではInstructorsテーブルの1行だけ更新すれば完了。

Prof.A の研究室が変わったのに、非正規化テーブルでは 2 行をすべて手作業で直さなければなりません。1つでも漏れると、「Prof.A は Room 301 でもあり Room 502 でもある」という矛盾が生まれます。3NF のテーブルなら、たった 1 行を直せば終わりです。

これが正規化のすべてです — あとは「どの列をどこに置けば、1か所にだけ保存されるのか」を判断するためのルールです。

関数従属 — 正規化の判断基準

正規化では、「この列はどこに依存しているのか」を考えます。これを 関数従属 といいます。

たとえば student_id → student_name は、「学生 ID がわかれば名前が決まる」という意味です。

下の図では、矢印の色に注目してください:

  • = 全体キーに依存(正常)
  • = キーの一部にだけ依存(部分従属 → 2NF 違反)
  • 赤の点線 = 非キー列を経由して依存(推移的従属 → 3NF 違反)
関数従属を一目で

Enrollment(student_id, course_id, student_name, course_title, instructor_id, instructor_office, grade)

student_id主キーcourse_id主キーstudent_namecourse_titleinstructor_idinstructor_officegrade
完全従属(キー全体に依存)部分従属(キーの一部に依存)推移従属(非キー列を介して依存)

黄色と赤の矢印をなくすこと = テーブルを分割すること = 正規化です。

1NF → 2NF → 3NF をまとめて見る

下で、段階ごとにテーブルがどう分割されていくかを実際にクリックしてみてください。赤いセルは、「この列はここにあってはいけない」という印です。

正規化ステップバイステップ
すべてが1テーブルに。1セルに複数の値。
Enrollment
student_id 🔑student_namecoursesgrades
S1KimMATH101, CS102A, B+
S2LeeCS102A
S3ParkMATH101, CS102, PHY201B, A, A-
問題: courses列に複数の値が1セルに。
解決: 学生-科目のペアごとに1行に分割。

1NF: 1つのセルに値は1つ

1つのセルに MATH101, CS102 のように複数の値を入れると、検索もできず、JOIN もできません。

ルール: すべてのセルは原子的(1つの値)でなければならない。

phone1, phone2, phone3 のような列も同じ問題です — 値を列名の中に隠しているだけです。

2NF: 部分従属の除去

複合キー (student_id, course_id) を持つテーブルでは、student_namestudent_id にしか依存しません。全体キーに依存しているのではなく、その一部にだけ依存しているわけです — これが 部分関数従属 です。

ルール: すべての非キー列は 全体キー に依存しなければならない。

部分従属があるなら、その列を取り出して別テーブルにします。

キーが単一列なら? 部分従属そのものが起こりえないので、1NF なら自動的に 2NF です。

3NF: 推移的従属の除去

Courses テーブルで instructor_officecourse_id(キー)に直接依存していません。course_id → instructor_id → instructor_office という経路を通ります — つまり、非キー列が別の非キー列に依存しているのです。これが 推移的従属 です。

ルール: 非キー列は キーだけを通じて 依存しなければならない。ほかの非キー列を経由してはいけない。

解決法: instructor_office を Instructors テーブルに分離する。

暗記の代わりに、1つの問い

「すべての非キー属性は キーに、全体キーに、キーのみに 従わなければならない。」

この一文がすべてです:

  • キーに = 1NF(事実がキーで識別できなければならない)
  • 全体キーに = 2NF(キーの一部ではなく全体に依存)
  • キーのみに = 3NF(非キー列を経由せず、キーに直接依存)

試験で正規化の問題が出たら、この問いだけを投げかけてください: 「この列は何に依存しているのか? 全体キーか、キーの一部か、それとも非キー列か?」

よくある勘違い

「3NF までやれば終わり」 — 3NF は良い出発点であって、終着点ではありません。より厳密な BCNF が必要な場合もありますし、逆に性能のためにあえて非正規化することもあります。

「テーブルはたくさん分割するほどよい」 — 正規化の目的はテーブル数を増やすことではなく、「各テーブルが1種類の事実だけを担当する」ようにすることです。分割すべき理由(従属関係)がないのに分割すると、JOIN が複雑になるだけです。

「NoSQL なら正規化は不要」 — 正規化は関係 DB 向けの用語ですが、「重複データの一貫性を保つ」という問題はどんな DB にもあります。非正規化するときも、「何を犠牲にしているのか」を理解したうえで行うべきです。

自分でチェックしてみよう

自分のプロジェクトのテーブルを1つ選んで、次を確認してみてください:

  1. キーが何かを書き出す。
  2. 各非キー列が全体キーに依存しているか、それとも一部にだけ依存しているかを確認する。→ 一部なら 2NF 違反。
  3. 非キー列がほかの非キー列に依存していないかを確認する。→ あれば 3NF 違反。

この 3 ステップだけで、ほとんどの正規化の問題は見つけられます。

問題の解き方でお困りですか?

問題をアップロードすると、検証済みのステップバイステップ解答が数秒で届きます。

GPAI Solver を開く →