RDBにおける非正規形から第3正規形までの変換

# 正規化
リレーショナルデータベース（RDB）での非正規形から第3正規形までの正規化についてまとめます。

## テーブルの各要素の名称
正規化に入る前にテーブルの各要素の名称について確認しておきます。
テーブルの各要素は様々な名称で呼ばれることがあります。

- テーブル全体の呼び方
  - テーブル、表
- 横方向の行の呼び方
   - 行、row、レコード、タップル
- 縦方向の列の呼び方
  - 列、column、フィールド

データベースの用語として用いる際は行（row）、列（column）を使用すべきです。

<img src= "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/3330d6e9-1b92-32c4-2ecc-610440727e40.png" width="70%"/>


## 非正規形（Unnormalized form）
第1正規形の規則を満たさない形です。
以下のようなテーブルは正規化されておらず、非正規形と呼ばれます。
![non-norm1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/aa6baf3c-3969-37f8-0339-7ee42a6486b4.png)


赤くハイライトした部分は、1つの行に2つの行が含まれています。この形は第1正規形を満たしていません。
第1正規形については次の節で説明します。
今後はこのテーブルを正規化していきます。

## 第1正規形（First Normal Form, 1NF)
第1正規化は以下の条件を満たすものです。


#### Rule 1
各列に含まれる値はそれ以上分けることができない値でなければいけません。
<img src= "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/05f2d52b-ed65-3751-ae04-86663210882c.png" width="70%"/>

#### Rule2

列に含まれる値のデータ型は同じでなければなりません。
<img src= "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/f8bf2cb4-2058-a098-97bb-98db96aef9b6.png" width="70%"/>

#### Rule3

同じテーブルの各列の名前に同じ名前を用いてはなりません。
![non-norm2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/c2944639-ceab-e3b4-6c2c-a35ab6f339a9.png)

#### Rule4
レコード（横の行）の並び順は意味を持ちません。また、カラム（縦の列）の並び順も意味を持ちません。


第1正規形のテーブルは次のようになります。
![1nf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/8ccba85b-0379-6025-d49c-b8c7b58b7eab.png)
一行に複数含まれていたデータを分割して2つの行にすることで、第1正規形を満たしました。
下線は主キーを表します。
主キーは以下の条件を満たします。

- 行を一意に識別できる
- Null値をとらない

今回のテーブルは`student_id`と`course_id`が決まればすべての行を識別できます。
`student_name`などは同じ値をとる可能性があるので、主キーにはなりません。

## 第2正規形（Second Normal Form, 2NF）
第2正規形は以下の条件を満たします。

#### Rule1
テーブルは第1正規形を満たす。

#### Rule2
テーブル内に部分従属性が存在しない。
すなわち、すべての非キー属性が主キー属性に完全に関数従属する。

### 関数従属性（Functional Dependency）

正規化は関数従属という用語が重要となります。
Aが決まればBも決まるとき、BはAに関数従属するといいます。
A -> Bと表すことができます。

例のテーブルで表すと、`student_id`が決まれば`student_name`も決まるので、`student_name`は`student_id`に関数従属するといえます。
また、`student_id`->`student_name`と表すことができます。

`grade`は`student_id`と`course_id`の両方が決まれば決定されるので、`grade`は`student_id`と`course_id`に関数従属するといえます。
また、`(student_id, course_id)` -> `grade`と表すことができます。

このテーブルに存在する関数従属性は次のようになります。

- `student_id` -> `student_name`
- `course_id` -> `course_name`
- `course_id` -> `teacher_name`
- `course_id` -> `teacher_phone_number`
- `teacher_name` -> `teacher_phone_number`
- `(student_id, course_id)` -> `grade`

![fd1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/8d966e37-f79d-0702-b5ea-64a16230ebd4.png)




### 部分関数従属性（Partial Functional Dependency）

主キー属性の一部がほかの非キー属性を決定する関数従属性です。
今回の例では、非キー属性の`grade`は主キー属性である`student_id`と`course_id`に完全に従属しているので部分関数従属性を持ちません。
一方で、非キー属性の`student_name`は主キー属性の一部である`student_id`にのみに従属しているため、部分関数従属性を持ちます。
また、非キー属性の`course_name`、`teacher_name`、`teacher_phone_number`も主キー属性である`course_id`にのみ関数従属しているため、部分関数従属性を持ちます。
従って、このテーブルは第2正規形を満たしていません。
そのため、部分関数従属性を持つ関係を別のテーブルに分割する必要があります。
分割する際は下図のように、青、緑、赤で色分けされた関係に分割します（オレンジ色の関数従属性は後述します）。
![pfd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/debe4b9b-60bc-58b7-eb9b-ffaa13850a98.png)

分割したテーブルは以下のようになります。
重複する行は取り除きます。
![norm2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/22d17668-f790-d302-e05d-4ca475ba3b60.png)
これで部分関数従属性は取り除かれ、第2正規形を満たしました。

## 第3正規形（Third Normal Form, 3NF）

第3正規形は以下の条件を満たします。

#### Rule1
テーブルは第2正規形を満たす。

#### Rule2
テーブル内に推移関数従属性が存在しない。

### 推移関数従属性（Transitive Functional Dependency）

非キー属性がほかの非キー属性を決定する関数従属性です。
先ほどの図中で示されたオレンジ色の`teacher_name` -> `teacher_phone_number`という関数従属性がありました。`teacher_name`と`teacher_phone_number`はどちらも非キー属性です。従ってこの関数従属性は推移関数従属性と言えます。

先ほど分割したテーブルに以下のように名前を付けます。
![norm2-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/a46ee554-a1e5-199e-228f-423d341ee4b0.png)


実は学生テーブルと履修テーブルはすでに第3正規形を満たしています。
教科テーブルのみオレンジ色に示した部分に推移関数従属性が存在します。
そこで、教科テーブルを分割します。
![3nf1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/f645ea92-db88-b0f0-0557-b270bf028d72.png)
このように分割できますが、`teacher_name`は名前が重複してしまう可能性があるので、新しい属性の`teacher_id`を用いてテーブルを作成します。
![3nf2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/799ea77e-1787-907d-899e-91f2e45332c0.png)
こうすることで第3正規形を満たすことができました。
最終的にはこのように正規化されました。
![3nf3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254347/7165f496-28a7-f446-48f5-c3743949be24.png)

最後に関数従属性のまとめを記載しておきます。

### 関数従属性のまとめ

- 関数従属性
  - 主キー属性 -> 非キー属性

- 部分関数従属性
  - 主キー属性の一部　-> 非キー属性

- 推移関数従属性
 - 非キー属性　-> 非キー属性