今更ながらテーブルの正規化についてまとめてみた

# はじめに
テーブル設計の機会があったので、改めて真面目に勉強してみた
本を読んでる最中は理解できてるつもりだったが、いざまとめてみると意外と理解できてなかった
※以下で使用している例は全て架空のデータです

# 第1正規化
## ルール
1つのセルには1つの値しか含まない（スカラ値の原則）

## 非正規形の例
（以下、*斜体*が主キー）

| *ユーザID* | ユーザ名 | 家族氏名 |
|:-:|:-:|:-:|
|  1 | 山田太郎  |  山田次郎,山田三郎 |
|  2 |  鈴木一郎 |  （なし） |

## 非正規形の問題点
主キーを指定しても値が一つに定まらない（関数従属でない）

## 正規形の例

| *ユーザID* | *ユーザ名* | *家族氏名* |
|:-:|:-:|:-:|
|  1 | 山田太郎  |  山田次郎 |
|  1 | 山田太郎  |  山田三郎 |
|  2 |  鈴木一郎 |  （なし） |

ただしこれだと、全ての列を主キーに指定する必要があるが、家族がいない場合もあるので
主キーとしての要件(NOT NULL)を満たしていないことになってしまう。
これを解消するには2つのテーブルに分解する

**ユーザテーブル**

| *ユーザID* | ユーザ名 |
|:-:|:-:|
|  1 | 山田太郎  |
|  2 |  鈴木一郎 |

**家族テーブル**

| *家族ID* | ユーザID | 家族氏名 |
|:-:|:-:|:-:|
|  1 | 1  |  山田次郎 |
|  2 | 1  |  山田三郎 |

# 第2正規化
## ルール
部分関数従属を認めない

## 非正規形の例

| *大学コード* | 大学名称 | *学籍番号* | 氏名 |
|:-:|:-:|:-:|:-:|
|  A |  東京大学 |  1 |  山田太郎 |
|  B |  京都大学 |  1 |  鈴木一郎 |

## 非正規形の問題点
- 大学名称は大学コードのみに従属する（部分関数従属：主キーの一部のみに従属）
- この状態だと、
    - 大学名称に変更があった場合、該当の大学コードの全ての大学名称を変更しなければならない
    - 学生が存在していない大学を登録することができない

## 正規形の例

**学生テーブル**

| *大学コード* | *学籍番号* | 氏名 |
|:-:|:-:|:-:|
|  A |  1 |  山田太郎 |
|  B |  1 |  鈴木一郎 |

**大学テーブル**

| *大学コード* | 大学名称 |
|:-:|:-:|
|  A |  東京大学 |
|  B |  京都大学 |

# 第3正規化
## ルール
推移的関数従属を認めない

## 非正規形の例

| *大学コード* | *学籍番号* | 氏名 | 学部コード | 学部名称 |
|:-:|:-:|:-:|:-:|:-:|
|  A |  1 |  山田太郎 |  a |  法学部 |
|  B |  1 |  鈴木一郎 |  b |  経済学部 |

## 非正規形の問題点
- 学部名称は学部コードのみに従属する（推移的関数従属：主キー以外のカラムに従属）
    - デメリットは第2正規化の時と同様

## 正規形の例

**学生テーブル**

| *大学コード* | *学籍番号* | 氏名 | 学部コード |
|:-:|:-:|:-:|:-:|
|  A |  1 |  山田太郎 |  a |
|  B |  1 |  鈴木一郎 |  b |

**学部テーブル**

| *学部コード* | 学部名称 |
|:-:|:-:|
|  a |  法学部 |
|  b |  経済学部 |

## 補足
- 部分関数従属と推移的関数従属は、どちらもテーブル内に従属性があるという点では同じだが、
- 部分関数従属は主キーの一部に従属、推移的関数従属は主キー以外のカラムに従属という点で異なる

# 第4正規化
## ルール
複数の多値従属性を認めない

## 非正規形の例

n:n対応の関連テーブルで発生する
学生:ゼミ = n:n
学生:講義 = n:n
の場合

| *学籍番号* | *ゼミコード* | *講義コード* |
|:-:|:-:|:-:|
|  1 |  A |  a |
|  1 |  A |  b |
|  2 |  B |  c |

## 非正規形の問題点
- 複数の多値従属性がある
    - 学籍番号ーゼミコード
    - 学籍番号ー講義コード
    - （ゼミと講義には関連がないものとする）
- この状態だと、
    - 学籍番号1の学生がゼミAからゼミBに異動する際、複数のレコードを修正しなければならない
    - 受講する講義が決まっていない学生はゼミを登録することができない

## 正規形の例

**学生ーゼミ関連テーブル**

| *学籍番号* | *ゼミコード* |
|:-:|:-:|
|  1 |  A |
|  2 |  B |

**学生ー講義関連テーブル**

| *学籍番号* | *講義コード* |
|:-:|:-:|
|  1 |  a |
|  1 |  b |
|  2 |  c |

## 補足
ゼミと講義にも関連がある場合は、以下の第5正規化に進む

# 第5正規化
## ルール
全ての関連について、対応する関連エンティティを作る

## 非正規形の例

以下で、ゼミ:講義=n:nの関係も成り立つ場合
（1つのゼミが複数の講義を開講していて、かつ1つの講義について複数のゼミが担当することがある場合）

**学生ーゼミ関連テーブル**

| *学籍番号* | *ゼミコード* |
|:-:|:-:|
|  1 |  A |
|  2 |  B |

**学生ー講義関連テーブル**

| *学籍番号* | *講義コード* |
|:-:|:-:|
|  1 |  a |
|  1 |  b |
|  2 |  c |

## 非正規形の問題点
- 関連エンティティが不足している
- この状態だと、
    - 関連を十分に表すことができていない
    - 学生がいないゼミ（悲しい）を登録することができない

## 正規形の例

**学生ーゼミ関連テーブル**

| *学籍番号* | *ゼミコード* |
|:-:|:-:|
|  1 |  A |
|  2 |  B |

**学生ー講義関連テーブル**

| *学籍番号* | *講義コード* |
|:-:|:-:|
|  1 |  a |
|  1 |  b |
|  2 |  c |

**ゼミー講義関連テーブル**

| *ゼミコード* | *講義コード* |
|:-:|:-:|
|  A |  a |
|  A |  b |
|  B |  c |

# 正規化のデメリット
正規化とは、テーブルを細かく分割していく作業に他ならない
ということは、元のテーブルに復元するには結合しなくてはならない
結合はパフォーマンス的にコストが大きい
正規化は極力進めたほうがいいが、他に手がない場合にはあえて冗長にもたせることも検討する

# 参考文献
- [達人に学ぶDB設計　徹底指南書](https://www.amazon.co.jp/%E9%81%94%E4%BA%BA%E3%81%AB%E5%AD%A6%E3%81%B6DB%E8%A8%AD%E8%A8%88-%E5%BE%B9%E5%BA%95%E6%8C%87%E5%8D%97%E6%9B%B8-%E3%83%9F%E3%83%83%E3%82%AF-ebook/dp/B00EE1XPAI)