第 2 章
テーブル設計と正規化
悪いテーブル設計の例
以下のテーブルを見てください。ユーザーと彼らが借りている本の情報が混在しています:
user_books テーブル(悪い例)
┌─────────┬────────┬──────────────────┬──────────────────┬──────────────────┐
│ user_id │ name │ book_title │ book_title_2 │ book_title_3 │
├─────────┼────────┼──────────────────┼──────────────────┼──────────────────┤
│ 1 │ 太郎 │ 徒然草 │ 源氏物語 │ 古事記 │
│ 2 │ 花子 │ 賢治全集 │ NULL │ NULL │
│ 3 │ 次郎 │ 昭和史 │ 平成の政治 │ 令和へ │
└─────────┴────────┴──────────────────┴──────────────────┴──────────────────┘
このテーブルの問題点
graph TB
A["悪いテーブル設計の問題"] --> B["📊 NULL の浪費"]
A --> C["🔄 データ更新が複雑"]
A --> D["📈 スケーラビリティ欠如"]
A --> E["🔍 検索が非効率"]
B --> B1["花子さんは本を1冊だけ借りているのに"]
B --> B2["book_title_2と_3がNULLで無駄"]
C --> C1["1ユーザーが借りた本を増やせない"]
C --> C2["同じユーザー情報が重複"]
D --> D1["新しい本を追加するたびにカラム追加必要"]
E --> E1["3冊目の本で検索できない"]
正規化とは
**正規化(Normalization)**は、データベース設計の最重要概念です。テーブルを複数に分割することで、以下を実現します:
- ✅ データの重複排除
- ✅ データ更新時の矛盾防止
- ✅ ストレージ効率の向上
- ✅ クエリの高速化
graph LR
A["非正規化テーブル"] -->|分割と整理| B["正規化されたテーブル群"]
B --> C["データ重複 ✗"]
B --> D["矛盾リスク ✗"]
B --> E["効率的 ✓"]
B --> F["保守性 ↑"]
正規化の段階
正規化には複数のレベル(正規形)があります。ほとんどの実務では 第3正規形(3NF) まで進めれば十分です。
第1正規形(1NF):原子性
各セルは1つの値のみ保有する という原則です。
❌ 非正規形の例
┌─────────┬────────┬──────────────────────────────┐
│ user_id │ name │ book_titles │
├─────────┼────────┼──────────────────────────────┤
│ 1 │ 太郎 │ 徒然草、源氏物語、古事記 │← 複数値が1セル
└─────────┴────────┴──────────────────────────────┘
✅ 第1正規形
┌─────────┬────────┐ ┌─────────┬──────────────┐
│ user_id │ name │ │ user_id │ book_title │
├─────────┼────────┤ ├─────────┼──────────────┤
│ 1 │ 太郎 │ │ 1 │ 徒然草 │
│ 2 │ 花子 │ │ 1 │ 源氏物語 │
└─────────┴────────┘ │ 1 │ 古事記 │
└─────────┴──────────────┘
第2正規形(2NF):部分関数従属の排除
主キーの一部に従属するカラムは分離する という原則です。複合主キーの場合に重要です。
❌ 非正規形の例
┌──────────────┬─────────┬─────────┬────────────┐
│ user_id, day │ user_id │ day │ sales($) │
├──────────────┼─────────┼─────────┼────────────┤
│ (1, 2026年6月20日) │ 1 │ 2026年6月20日 │ 5000 │
│ (1, 2026年6月21日) │ 1 │ 2026年6月21日 │ 3000 │
└──────────────┴─────────┴─────────┴────────────┘
↑ user_idだけで決まるので、dayに従属していない
✅ 第2正規形
users テーブル sales テーブル
┌─────────┐ ┌─────────┬────────────┬──────────┐
│ user_id │ │ user_id │ day │ sales($) │
├─────────┤ ├─────────┼────────────┼──────────┤
│ 1 │ │ 1 │ 2026-06-20 │ 5000 │
└─────────┘ │ 1 │ 2026-06-21 │ 3000 │
└─────────┴────────────┴──────────┘
第3正規形(3NF):推移関数従属の排除
主キー以外のカラム間に従属関係がないようにする という原則です。
❌ 非正規形の例
┌──────────┬────────┬───────────┬───────────┐
│ book_id │ title │ author_id │ author │
├──────────┼────────┼───────────┼───────────┤
│ 101 │ 古事記 │ A │ 太郎 │
│ 102 │ 源氏物語│ B │ 花子 │
└──────────┴────────┴───────────┴───────────┘
↓ author_id → author の従属性
✅ 第3正規形
books テーブル authors テーブル
┌──────────┬────────┬───────────┐ ┌───────────┬────────┐
│ book_id │ title │ author_id │ │ author_id │ author │
├──────────┼────────┼───────────┤ ├───────────┼────────┤
│ 101 │ 古事記 │ A │ │ A │ 太郎 │
│ 102 │ 源氏物語│ B │ │ B │ 花子 │
└──────────┴────────┴───────────┘ └───────────┴────────┘
正規化されたテーブル設計の例
ユーザー・注文・商品の関係を適切に設計したモデル:
users テーブル
┌─────────┬────────┬──────────────────┐
│ user_id │ name │ email │
├─────────┼────────┼──────────────────┤
│ 1 │ 太郎 │ taro@example.com │
│ 2 │ 花子 │ hanako@ex.com │
└─────────┴────────┴──────────────────┘
orders テーブル
┌──────────┬─────────┬─────────────┐
│ order_id │ user_id │ order_date │
├──────────┼─────────┼─────────────┤
│ 101 │ 1 │ 2026-06-20 │
│ 102 │ 1 │ 2026-06-21 │
│ 103 │ 2 │ 2026-06-22 │
└──────────┴─────────┴─────────────┘
order_items テーブル
┌──────────┬────────────┬──────────┐
│ order_id │ product_id │ quantity │
├──────────┼────────────┼──────────┤
│ 101 │ 10 │ 2 │
│ 101 │ 20 │ 1 │
│ 102 │ 10 │ 3 │
└──────────┴────────────┴──────────┘
products テーブル
┌────────────┬──────────────┬───────┐
│ product_id │ product_name │ price │
├────────────┼──────────────┼───────┤
│ 10 │ りんご │ 150 │
│ 20 │ みかん │ 120 │
└────────────┴──────────────┴───────┘
メリット
graph TB
A["正規化されたテーブル設計"] --> B["データ重複がない"]
A --> C["更新が簡単"]
A --> D["一貫性が保たれる"]
A --> E["拡張が容易"]
B --> B1["ストレージ効率 ↑"]
C --> C1["1つの場所だけ修正"]
D --> D1["矛盾の可能性 ↓"]
E --> E1["新しいテーブルを追加可能"]
正規化のバランス
正規化は重要ですが、過度な正規化はパフォーマンス低下につながります。実務では、第3正規形と実装バランス を取ることが重要です。
┌─────────────────────────────────┐
│ 正規化レベル vs パフォーマンス │
│ │
│ 正規化レベル ↑ │
│ │ / │
│ │ / ← 最適点 │
│ │ /\ │
│ │ / \ │
│ ───────────── │
│ パフォーマンス │
│ │
│ 結論:第3正規形 + 必要に応じ │
│ てデータキャッシュ │
└─────────────────────────────────┘
まとめ
- 正規化はデータベース設計の根本原則です
- 第1~第3正規形の段階的なプロセスで、データの一貫性と効率性を両立します
- ほとんどの実務では 第3正規形(3NF) で十分です
- 過度な正規化は避け、実装バランスを考慮 することが大切です
次の章では、正規化されたテーブルに対して SQL を使ってデータを操作する方法を学びます。