具体的なイメージで「データベースの正規化」を分かりやすく説明（したかった）

# この記事は何か
SEならだれでも一度は勉強する(よね？) __データベースの正規化__。でも、 __関数従属とか言われてよく分からなくなる__ こと、ありますよね。

ということで、 __もう少し具体的なイメージやキーワード__ を使って、分かりやすく記憶に残る説明ができないか、と考えて書いてみたのがこの記事です。

例えば、「 __第2正規化__ ってなに？」と聞かれたときに「 __部分関数従属の解消__ っすよね」と即答するのは、なかなか難しいと思うのです。

でも、この記事を読んでおくと「えーと。なんか細かくは覚えてないっすけど、__明細テーブルからヘッダテーブル取り出せ、みたいなイメージのやつ__ っすよね」くらいの回答ができるようになります。少なくとも、そうなってほしいと思って書いています。ならなかったらスミマセン。


対象読者はSE 3年目くらい(DB、テーブル、レコードといった言葉が分かる方)を想定しています。

全体がとんでもなく長くなってしまったので、以下に各章へのリンクを貼っておきます

# 各章へのリンク
[はじめに  ：前提の説明など](#0-はじめに)  
[第1正規形  ：セルを結合するな、ひとつのセルに複数の値を入れるな、全部バラせ](#1-第1正規形)  
[第2正規形  ：明細テーブルからヘッダテーブルを取り出せ](#2-第2正規形)  
[第3正規形  ：マスタ化できる列をマスタ化して取り出せ](#3-第3正規形)  
[ボイス-コッド正規形  ：より絞り込みの強い列で主キーを組みなおして取り出せ](#b-ボイス-コッド正規形)  
[第4正規形  ：単純掛け合わせテーブルを作るな](#4-第4正規形)  
[第5正規形  ：単純掛け合わせテーブルを作るな](#5-第5正規形)  
[第6正規形  ：非キー列は1列だけ](#6-第6正規形)  


# 0. はじめに
各正規形の説明の前に、用語の準備や、本記事の参考情報を記載しておきます。

## 0-1. テーブルとその構成要素
本記事では、テーブルの構成要素として、レコード、列、セル、主キーという言葉を用います。レコード、列、セルのイメージは以下の通りです。（主キーについては後述します）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/6166221d-f58b-79fd-8c91-da6c0a997d05.png)

## 0-2. 主キーとは
主キーとは、その値を指定すれば、必ず1行のレコードを特定できるような列の組合せです。一つのテーブルに対して必ず一つ存在しなければならず、かつ、一つしか存在しません。

例えば、以下はチームと所属メンバーのテーブルですが、主キーは __チーム__ と __メンバー__ です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/e7af29c8-c3ff-9121-0e52-f093a7becece.png)

チームだけではどのレコードか特定できず、  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7e37eec6-dc15-1a67-03f7-93d3a5f45ca1.png)

メンバーだけでもどのレコードか特定できませんが、  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f11ec6f5-c142-76cb-84ca-dc9873b9cf8c.png)

両方を指定すると、レコードが特定できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/d089317a-bc03-a875-1013-d2d08c5cadf0.png)

## 0-3. 単純掛け合わせテーブルについて
:::note info 
本記事を初めて読む方は、この章をスキップして先へ進んでください。もし本記事の後半を読んでいて「あれ？単純掛け合わせってなんだ？」と思ったら、ここに戻ってくればよいです。(特に疑問を抱かずに最後までこの記事を読めたなら、この章を読む必要はありません)
:::
本記事では、後半に「単純掛け合わせ」という表現が出てきます。  
これは、正確には「自然結合(Natural Join)」と呼ばれる処理で以下のように説明できます。 


### SQLを知っている方への説明

例えば以下の2テーブルに対して、

チーム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/3ee77eaf-cc50-2d13-793b-e8c0a9cc346b.png)

チームメンバー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/640b73c9-f0c0-ecb2-91e4-6a5569a3b5a6.png)


以下のように、 __両テーブルに存在する列を一致条件に、全列を取得するSQLを実行した結果__ です。  
```SQL:SQL
select T1.チーム名, T1.担当, T2.メンバー, T2.学年 -- 全ての列を羅列。両テーブルにある列は一方から取得
from チーム T1, チームメンバー T2
where T1.チーム名 = T2.チーム名  -- 両テーブルに存在する列を一致条件に指定
```

結果は以下になります。

単純掛け合わせ結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/828c6692-809e-1760-64a5-56b85a39468a.png)

### SQLを使わない説明

例えば以下の2テーブルがある場合に、両方に共通する列「チーム名」を使って、「チーム」に「チームメンバー」の列を追加していきます。

チーム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/54b9e20d-49ec-96a6-38a8-29ca7fa62c95.png)

チームメンバー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/84e8202d-e881-2b85-57cb-8ad18dd3602c.png)

まず、チーム名が「トラ」のレコードです。  
以下のように、チームメンバーの列(すでに存在するチーム名を除く)を追加します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/ddc8c706-c612-e896-3910-f73d0668c983.png)


次に、「サル」です。
サルの場合、「チーム」1レコードに対し、「チームメンバー」2レコードが存在します。  
このように、一方のレコードが少ない場合、多い方のレコード数に合わせて複製してから、列を追加します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/a7fc7d1b-f475-3708-0263-c8bb5b8ab0cc.png)

最後に「アライグマ」です。  
アライグマの場合、チームメンバーにレコードが存在しません。  
このように、一方にしかレコードが存在しない場合は、全体からレコードを削除します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/a0d1136a-c604-b93d-5c03-87a1b01f3a35.png)

出来上がりは以下となります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/828c6692-809e-1760-64a5-56b85a39468a.png)

## 0-4. 参考書
本記事の内容は私が起こしたものですが、用語の定義や使い方については、なるべく、以下の書籍にあわせています。

[達人に学ぶDB設計 徹底指南書 Kindle版 (ミック著)](
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)

正直、この書籍が手放しでお勧めできるかといわれると、いろいろ思うことはあるのですが、DBの初学者が使いやすそうな書籍で、最低限まとまっているものが、私の調べた範囲ではこれくらいしか見当たらず。


ということで、より詳細な内容が知りたい方は、本記事を読んだ後に、上の書籍を読んでみてもいいかもです。（2024年10月現在、既に2版が出ているようなので、これから購入するならそちらがよいかと思います）








# 1. 第1正規形
実は、正確な説明が一番難しいのは第1正規形ではないかと思ったりします。
ですがまあ、それはそれとして説明を始めていきます。

## 1-1. 第1正規形のイメージ
第1正規形(第1正規化)のイメージは __「セルを結合するな、ひとつのセルに複数の値を入れるな、全部バラせ」__ です。
### セルを結合するな。バラせ
例えばこういう結合セルがあったら  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/5147fcca-4cb0-9168-9af3-1ea3402ad796.png)

こんな感じにバラして、各行に同じ値を埋めます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/d2f25b08-6ba1-2a46-b600-3b9cd97f9bc3.png)

### ひとつのセルに複数の値を入れるな。バラせ
例えば、こんな感じで一つのセルに複数の値を持つセルがあったら  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/e40aa18c-f5a6-04ad-7e44-4891c738caa4.png)

こんな感じで行にバラします。(他の列にはもともとの値をそのままコピーします)  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/4ae65a7e-a780-e488-3df2-008de8243bb5.png)

## 1-2. なぜ第1正規形が必要なのか
いきなりアレですが、なぜ第1正規形が必要かを説明するのは結構難しいです。
ここでは、そもそもRDBが想定している「テーブル」は、セルの結合がなく、一つのセルに複数の値が入っていることもないのが大前提だから、という説明にさせてください。身も蓋もなくてスミマセン。  

## 1-3. もう少し正確な説明
第1正規形の定義は __「一つのセルの中には一つの値しか含まない」__ というものです。
これについては、あまり追加説明の必要はないかと思います。一つ目のイメージのようにセルを結合されていると、「一つのセル」が何なのか分からなくなるし、二つ目のイメージは定義そのものなので。

## 1-4. 補足
二つ目のイメージは以下のように列を横に展開してもよいのでは、と思った方がいるかもしれません。これも「一つのセルの中には一つの値」ではあるので。実際、参考書では、これを第1正規形として認めています。(私は認めない派ですが)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/8686cf89-9f38-7253-8d38-a421b4803ca8.png)

ですが、このレイアウトは、仮に第1正規形と認めたとしても、明確なアンチパターン[^101]で、基本的に採用すべきではないです。同じ意味のものは一つの列のみ用意し、複数出現する場合は、レコードを追加して格納してください。

[^101]: マルチカラムアトリビュートという、立派な(?)アンチパターン名がついています。例えば、必要な列数(子供は何人までいるか)は、簡単には決められないですよね。(ギネス記録は69人らしいですが、養子とか考えると、無限に増やせそう)。あと、「〇〇さんの親」を探すSQLが絶望的にめんどくさい感じになります。















# 2. 第2正規形
主キー列と非キー列の関係を整理する正規形です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/acf8a9e0-0339-896f-33ef-d389508c34cc.png)
			

## 2-1. 第2正規形のイメージ
第2正規形(第2正規化)のイメージは __「明細テーブルからヘッダテーブルを取り出せ」__ です。

### 明細テーブルからヘッダテーブルを取り出せ
例えば、以下のテーブルを考えます。いつ・だれが・どんな商品を・いくつ注文したかを管理するテーブルです。

注文明細テーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/95e55a85-2764-d5b1-b827-d862b142b407.png)

なお、注文番号・明細番号の定義は以下とします。
- 注文番号：注文ごとに付与する一意な番号。この番号が同じなら同じ注文。違うなら別の注文
- 明細番号：各注文に対して、その商品が明細の何番目に載っていたかを表す番号


ここで、注文日・利用者・利用者連絡先・注文番号に注目すると、同じデータが複数行に渡って繰り返し格納されていることに気付くかと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/1750988c-efe5-87ea-6c9d-ae7d5cd4d233.png)

これは当然で、一回の注文において、注文日や利用者(注文者)等は、じゃがいもやタマネギの各明細レコードに依存せず、共通の値を取るからです。これらの「明細に依存しない列」を、以下のように1注文1レコードの「注文テーブル」に取り出して、繰り返しをなくすのが第2正規化です。

注文テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7937d214-1d7a-bb80-3610-c4521321f480.png)   
※重複レコードを削除し、一意なレコードのみを残します。1注文1レコードになり、繰り返しが解消されます。

注文明細テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/84f75fe6-eba4-c6eb-c86d-25c1f0034c05.png)  
※別テーブルへ分解した列は、元テーブルから削除します。ただし、どの注文かが分かるように注文番号だけは残します。

この注文テーブルのように、明細に依存しない共通データを持つテーブルを、明細テーブルに対して「ヘッダテーブル」や「見出しテーブル」のような呼び方をします。  
ですので、第2正規化は __「明細テーブルからヘッダテーブルを取り出せ」__ と言いかえることが可能です。  
もちろん、これだけが第2正規形の作り方ではないのですが、実際の業務でもこのパターンは頻出するので、一旦これが第2正規化のイメージだ、と捉えてみてよいと思います。

## 2-2. なぜ第2正規形が必要なのか
第2正規形になっていないと、例えば1回の注文のデータが複数のレコードで保持されるため、以下のように矛盾したデータが作れてしまうからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/c9129c66-e2ef-9fb6-3c4e-0371d00ff9fa.png)


第2正規形になっていれば、1回の注文のデータは1レコードになっている(分解したときに重複レコードを削除した)ため、矛盾データが発生しません。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/dff83ebc-05f2-0668-3878-76977c5ca9e3.png)

このように、 __本来1レコードで管理できるはずのデータを、複数行にわたって繰り返すことで矛盾データが作れてしまう、というのは正規化で非常に重要な観点__ です。複数列にわたって同じデータの繰り返しを見つけたら、非正規形になっていないか疑ってみる必要があります。

## 2-3. もう少し正確な説明
第2正規形の定義は、そのテーブルが第1正規形、かつ、 __「部分関数従属が解消されていること」__ です。  
__関数従属__ とは、ある列(複数列の組合せも可)の値が決まると、他の列の値が決まることです。  
__部分関数従属__ は、主キーの一部の列に対して関数従属する列があることです。

以下、イメージで使用したテーブルを用いて、説明してみます。
まず前提として、このテーブルは一つのセルに一つの値しかないため、第1正規形です。

注文明細テーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/95e55a85-2764-d5b1-b827-d862b142b407.png)

既に説明したとおり、注文番号・明細番号の定義は以下です。
- 注文番号：注文ごとに付与する一意な番号。この番号が同じなら同じ注文。違うなら別の注文
- 明細番号：各注文に対して、その商品が明細の何番目に載っていたかを表す番号

この2列は、注文明細テーブルの __「主キー」__ になっています。この2列の値が決まると1行が特定できるからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/0aa2a524-803d-537a-3e66-21208ba3bc38.png)

まず、 __関数従属__ です。  
__関数従属__ は「ある列(複数列の組合せも可)の値が決まると、他の列の値が決まること」でした。  

例えば、注文番号・明細番号の組合せ、つまり、そのテーブルの __主キーはすべての列を関数従属させる__ と言えます。主キーの値が決まると、1行が特定されるので、すべての列の値が決まるからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/5e7e766c-374e-3cb7-cb50-f6a12bcf9829.png)

では、注文番号だけではどうでしょうか。  
例えば、注文番号は、商品を関数従属させません。注文番号が決まっても、商品の値は決まらないからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/5f1fcc97-99f2-6a32-ecba-ca7a0e6bc858.png)

しかし、注文番号は、注文日・利用者・利用者連絡先を関数従属させます。複数のレコードにまたがってはいますが、注文番号が決まると、これらの列の値が決まるからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/9530283c-f27a-7dec-0c01-92e36d16acb7.png)


次に、 __部分関数従属__ です。  
__部分関数従属__ は、「主キーの一部の列に対して関数従属する列があること」でした。  

既に見たように、主キーの一部である注文番号は、注文日・利用者・利用者連絡先を関数従属させます。このテーブルには __部分関数従属__ が存在します。  
これを、注文番号・注文日・利用者・利用者連絡先の4列からなるテーブル(注文テーブル)に取り出すことで解消するのが、第2正規化です。

注文テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7937d214-1d7a-bb80-3610-c4521321f480.png)   
※ 重複レコードを削除し、一意なレコードのみを残します。

注文明細テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/84f75fe6-eba4-c6eb-c86d-25c1f0034c05.png)  
※ 原因となる列を取り出すことで、部分関数従属が解消します。



## 2-4. 補足
この記事では、参考書に合わせて、第2正規化を「__主キー__ に対する部分関数従属を解消する」というイメージで記載しています。が、厳密には「__すべての候補キー__ に対する部分関数従属を解消する」が正しいです。  
参考書では、おそらく、分かりやすさを優先して主キーで説明しており、本記事も同じ方針で記載していますが、正確に理解したい方は、例えば以下の記事を確認してください。

[正規化についてまとめてみた](https://qiita.com/kihara-takahiro/items/703c1863248659ad518b)

以降、第3正規化、ボイスコッド正規化についても、「主キー」で説明している部分は、厳密には「候補キー」が正しいです。

## 2-5. 補足2
第2正規形を作る例としてもう一つ典型的なのが、__関連テーブルの分解__ です。前述のヘッダテーブルを取り出す処理と合わせて、第2正規化は大体こいつらです。  
詳しく説明しませんが、以下に例を書いておきます。気になる人は、「2-3. もう少し正確な説明」に沿って、自分でも正規化してみてください。  

__関連テーブルの分解__ は、AとB(例では生徒と教科)の関連を管理している1テーブルを、AとBと関連の3テーブルに分解するイメージです。 

○正規化前  
履修情報テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f40a96ef-10ff-7135-295a-4a775556e5c4.png)


○正規化後  
学生テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/2d318dee-53cd-5291-4ee3-00a9b135a61a.png)  
教科テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b6f2d7a3-d74f-b4d8-e1fe-b7c0e9eef2d6.png)  
履修テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/79cec22f-7c19-2f0e-8eb9-26b6f01dc019.png)






# 3. 第3正規形
第2正規形では主キー列と非キー列の関係を見ましたが、第3正規形では非キー列同士の関係を見ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/df9bad7d-b986-e47d-f6b7-1516fc459019.png)


## 3-1. 第3正規形のイメージ
第3正規形(第3正規化)のイメージは __「マスタ化できるものをマスタ化して取り出せ」__ です。

### マスタ化できるものをマスタ化して取り出せ

以下は第2正規形で分解して残った注文明細テーブルです。一見、第2正規形に分解する前のような、複数列で同じ値を繰り返すデータは無いように見えます。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/697830b1-c569-79dd-85b3-04b51154a7b9.png)

しかし、全体を商品列で並び変えてみるとどうでしょうか。実は、商品・商品分類・単価で同じデータの繰り返しが発生しています。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/707ad567-8df4-d30d-b54a-136b8ce560bf.png)

これも当然で、どの注文のどの明細であっても(もっと言えば、注文がなく明細が0行でも)、例えば、じゃがいもは野菜で330円だからです。  
このように、そもそも当該テーブルから独立している情報を管理する列を、以下のように別テーブルに取り出すのが第3正規化です。

商品テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/961e8455-0233-d5b6-cf7f-d6718b4c4838.png)  
※重複レコードを削除し、一意なレコードのみを残します。1商品1レコードになり、繰り返しが解消します。

注文明細テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f61e1d1d-94a4-87f2-c082-69ad1e6469e4.png)  
※別テーブルへ分解した列は、元テーブルから削除します。ただし、どの商品かが分かるように商品だけは残します。

この商品テーブルのように、実際の業務(注文など)から独立して、事前に決めておく情報(商品単価など)を保持するテーブルを、一般に「マスタテーブル」と呼びます。
ですので、第3正規化は __「マスタ化できるものをマスタ化して取り出せ」__ と言いかえることが可能です。  
第2正規形の場合と同じく、これだけが第3正規形の作り方ではないのですが、やはり頻出パータンではあるので、一旦これが第3正規形のイメージだ、と捉えてよいと思います。

## 3-2. なぜ第3正規形が必要なのか
第3正規形になっていないと、例えばひとつの商品のデータが複数のレコードで保持されるため、以下のように矛盾したデータが作れてしまうからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b7f44c5e-0ef9-2c8b-d545-921c4d9b60be.png)

第3正規形になっていれば、1商品のデータは1レコードになっている(分解したときに重複レコードを削除した)ため、矛盾データが発生しません。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/2a224c12-b3fe-b667-1f13-7553e5c163c6.png)

## 3-3. もう少し正確な説明
第3正規形の定義は、そのテーブルが第2正規形、かつ、 __「推移的関数従属が解消されていること」__ です。
__関数従属__ は「ある列(複数列の組合せも可)の値が決まると、他の列の値が決まること」でした。
__推移的関数従属__ は「主キー以外の列の間で関数従属が発生していること」です。

以下、イメージの説明で使用した注文明細テーブル(第2正規化済み)を用いて説明してみます。

先に確認したように、このテーブルにおいて、商品が決まると商品分類・単価が決まります。したがって、商品は商品分類・単価を関数従属させます。  
さらに、商品・商品分類・単価はいずれも主キーではありません。よって、このテーブルには、__推移的関数従属__ が存在します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/22aa8e19-5b45-8d8d-fab9-5dd58b7bf332.png)

これを以下のように分解し解消するのが、第3正規形(第3正規化)です。

商品テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/961e8455-0233-d5b6-cf7f-d6718b4c4838.png)  
※重複レコードを削除し、一意なレコードのみを残します。

注文明細テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f61e1d1d-94a4-87f2-c082-69ad1e6469e4.png)  
※別テーブルへ分解した列は、元テーブルから削除します。ただし、どの商品かが分かるように商品だけは残します。商品が関数従属させる列を取り出すことで、推移的関数従属が解消します。

## 3-4. 補足
本記事では、推移的関数従属を「主キー以外の列の間で関数従属が発生していること」と定義しましたが、これだと何が「推移的」なのか分からないですよね。実はこれ、あまり一般的な定義ではなく、通常は以下のように定義します。(参考書の定義も、おおむねこちらです)

- __推移的関数従属__ とは、以下のように推移的な(≒2段階の)関数従属が存在すること。  
K：主キー(複数列の組合せ可)  
A1：主キー以外の列 (複数列の組合せ可)  
A2：KとA1のいずれにも含まれない列(複数列でもいいが、1列あれば十分) 
ここで、KはA1を関数従属させる。さらに、A1はA2を関数従属させる。

具体的には以下のとおりです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/9e95f100-a5ea-53b3-a07a-e45a054478e1.png)

ただ、2-3.で見たように、主キーはそのテーブルすべての列を、必ず関数従属させますので、第1段階の条件は省略可能です。ということで、この記事では第2段階の条件のみを使って、「主キー以外の列A1が、主キーでもA1でもない列A2を関数従属させる」=「主キー以外の列の間で関数従属が発生している」ことを定義に採用しています[^301]。

[^301]: とはいえ、「主キー以外の列の間で関数従属が発生している」の「主キー以外」という部分を正確に定義しようとすると、結局、K(1段目)の評価が必要なので、省略というよりは言い換えといった方が正しいかもしれません。












# B. ボイス-コッド正規形
第2正規形とは反対に、非キー列からキー列への関係を整理する正規形です。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/8724ee88-753e-65ad-d38f-c3c7c6bd6269.png)

## B-1. ボイス-コッド正規形のイメージ
ボイス-コッド正規形(ボイス-コッド正規化)のイメージは __「より絞り込みの強い列で主キーを組みなおして取り出せ」__ です。

### より絞り込みの強い列で主キーを組みなおして取り出せ

以下は体験入学の受講学部・講義を管理するテーブルです。
受講者は体験する学部を選択し、その学部の講義を一つだけ受講できるものとします。
この時、このテーブルの主キーは、受講者と学部になります。

体験入学受講テーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/68c07467-e1e7-a2ba-fef9-8f7616523cc6.png)

学部と講義で並べ替えると分かりますが、このテーブルにも複数列をまたいだ同じデータの繰り返しが見つかります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/53e345f9-6297-5d47-5393-9ecc94cc6a24.png)
これは、講義が決まると学部が決まるためで、例えば講義が「環境工学I」なら学部は必ず「工学部」となり、この組合せが繰り返してしまうからです。

ということで、第2正規化や第3正規化と同様に、値が繰り返すこの2列を、以下のように別テーブルへ取り出すことを考えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/876385e9-0f55-1d0b-318d-17f9bc07b701.png)
※重複しているレコードは削除し、一意なレコードのみを残す。

ところが、元のテーブルで主キー列だった「学部」はこのテーブルの主キーになれません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/fc0d3777-bce3-10d8-e5b4-a4263d63ff04.png)

このテーブルの主キーは、元のテーブルでは非キー列だった「講義」です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/fc68d237-de4e-32fa-9b1e-64ff4be300cd.png)



このように、主キー列「学部」と、非キー列「講義」に以下が成立する場合に  
- 「学部」は「講義」を1件に絞り込めない
- 「講義」は「学部」を1件に絞りこめる ( __※非キー列の方が絞り込みが強い!__ )

まず、以下のように主キーを絞り込みが強い方に入れ替え、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/172408df-d0c2-fe09-84cf-7fbf258c4171.png)

さらに、以下のように分解するのがボイス-コッド正規化のイメージです。

受講テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b48e7647-5926-1249-9c89-2348e4e14234.png)

講義テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/edb6837a-0a28-a2d0-bb11-b2d9641db27f.png)  
※重複しているレコードは削除し、一意なレコードのみを残します

ボイス-コッド正規化の __「より絞り込みの強い列で主キーを組みなおして取り出せ」__ のイメージが伝わりますでしょうか。


## B-2. なぜボイス-コッド正規形が必要なのか
ボイス-コッド正規形に分解していないと、以下のように、矛盾したデータを作れてしまうからです。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/61d14cdc-8486-c499-b6df-ba84f310002b.png)

ボイスコッド正規形になっていれば、学部と講義の対応は1行で管理されるので、矛盾が発せしません。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/275648d6-e2fa-3cd2-c5a6-ce5d46ea385c.png)


## B-3. もう少し正確な説明
ボイス-コッド正規形の定義は、そのテーブルが第3正規形、かつ、 __「非キー列からキー列への関数従属が解消されていること」__ です。

以下、イメージで挙げたテーブルを例に説明してみます。  
体験入学受講テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/ab869f09-79ab-415b-57f3-8e9311bfdfa6.png)  

まず、細かく説明しませんが、このテーブルは第3正規形です。  
- 一つのセルの中には一つの値しか含んでいない ⇒ 第1正規形
- 主キーの構成列である「受講者」と「学部」は、どちらも単独では「講義」の値を決定できない ⇒ 部分関数従属はない ⇒ 第2正規形
- 非キー列は一つしかないので、非キー列間の関数従属はない ⇒ 推移的関数従属はない ⇒ 第3正規形

さて、 __関数従属__ は「ある列(複数列の組合せも可)の値が決まると、他の列の値が決まること」でした。
既にみたように、このテーブルにおいて、非キー列である「講義」の値が決まると、主キー列である「学部」の値が決まります。このテーブルには __非キー列からキー列への関数従属__ が存在します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/92bdee6d-7a10-e74b-aeda-8614c330b440.png)

これを、まず主キーを入れ替えることで、非キー列から主キー列への関数従属をなくします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/a00fccdf-6388-5d15-f9eb-a539da875314.png)

しかし、このままでは、主キーの一部である「講義」の値が決まると「学部」の値が決まる、すなわち、部分関数従属が発生します。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/3713d9f8-8cb2-2814-535a-651b3c1beaaa.png)

そこで、第2正規化を行い、以下の2テーブルに分解します。

受講テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b48e7647-5926-1249-9c89-2348e4e14234.png)

講義テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/edb6837a-0a28-a2d0-bb11-b2d9641db27f.png)  

これが、ボイス-コッド正規化のもう少し正確な説明です。


## B-4. 補足
ぶっちゃけ、ボイス-コッド正規化については、あえて正規化しないことの方が多いです。

一般にボイス-コッド正規化を行ったテーブルは、本来そのテーブルで管理すべき最も重要な対応関係を、失ってしまうことが多いからです。

例えば、体験入学について、以下の条件が追加になったとします。

- 各講義について、受講者をサポートするサポーターをつける
- サポーターは各講義に複数名割り当てるが、一人の学生には一人のサポーターが専任で対応する
- サポーターが複数の講義を掛け持ちすることはない(サポーターが決まれば講義が決まる)

このとき、以下のような体験入学受講テーブルを作れます

体験入学受講テーブル  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/9dd4bb7b-6def-fa6e-e094-f6283ebfe3f4.png)

そして、これをボイス-コッド正規化すると以下のようになります。

受講者担当サポーターテーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f28f8a77-1901-d8f2-867b-050ff783f119.png)

サポーター受け持ち講義テーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/3d6a18cd-b33a-f050-ff96-b095b4be897b.png)

でも、これ、 __元のテーブルレイアウトの方が自然__ じゃないですか？

例えば、受講者が受講する講義は決めたけど、まだサポーターは決めていない、という状況を、 __正規化後のテーブルは表現できません__ 。本来、メインとして管理されるべき受講者と講義の対応が、サポーターという副次的な要素を介してしか結び付けられないから[^3501]です。  

[^3501]:この問題は、テーブルの主キーは受講者と講義にして、サポーターは受講者・講義に関数従属している方が自然ではないか、と言い換えることもできます。この観点で、 __ボイス-コッド正規化は元のテーブルの関数従属を喪失する__ のように表現することがあります。

この辺りは、正規化というよりは、データモデリングの話になってくるので、これ以上記載しませんが、ボイス-コッド正規化を行うかは、慎重に判断する必要があります。  
とはいえ、第3正規化まで行ったテーブルが、ボイス-コッド正規形になっていないことは、かなり稀ですが。









# 4. 第4正規形
主キーのみのテーブルに対し、主キー列同士の関係を整理する正規形です。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/1ed28167-d414-e2f7-95ab-127512418886.png)


## 4-1. 第4正規形のイメージ
第4正規形(第4正規化)のイメージは __「単純掛け合わせテーブルを作るな」__ です。

### 単純掛け合わせテーブルを作るな
通常、正規化は、非正規形のテーブルを正規化する順で説明しますが、第4正規形は逆順の方が分かりやすいです。  

例えば、以下のように、チームと担当顧客、および、チームに所属する社員を管理しているテーブルがあるとします。

チーム所属社員マスタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7b28a8f5-fb5c-f484-043b-bed7b8f99136.png)

チーム担当顧客マスタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/e3ea787c-8476-5134-3e92-89ba7483259f.png)


この時、これらのテーブルを単純に掛け合わせた[^401]、以下のようなテーブルを考えることができます。

チーム社員顧客マスタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/c11ff950-8fc2-c190-6d80-a1df2dcbf2cd.png)

[^401]: 両テーブルに共通する「チーム」列をキーとして、以下のSQLで結合するイメージです。
select T1.チーム, T1.社員, T2.顧客   
from チーム所属社員マスタ T1, チーム担当顧客マスタ T2   
where T1.チーム = T2.チーム  

このテーブルでも、例えば[「後藤さん」が「チームA」に所属していることは分かるし、「チームB」が「●●株式会社」を担当していることも分かります。
さらに、このテーブルは主キーのみで構成されるため、ボイス-コッド正規形になっています。(第2正規形、第3正規形、ボイス-コッド正規形が、非キー列を評価していたことを思い出してください)  
では、このテーブルでチームと所属社員と担当顧客の対応を管理していいのか。__ダメ。元の2テーブルに分解して管理しなさい__、というのが第4正規形です。


## 4-2. なぜ第4正規形が必要なのか
第4正規形に分解していないと、以下のように、矛盾したデータを作れてしまうからです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/96afe98d-14e5-162e-7794-4f97dd92a8e6.png)

もとの2テーブルに分解していれば、社員ごとに複製されたレコードが無いため、このような矛盾データは作れません。 

チーム担当顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/fae30c6d-09df-a79a-c015-e78ed1a7653a.png)  

## 4-3. もう少し正確な説明
第4正規形の定義は、主キーのみのテーブルに対して __「多値従属が解消されていること」__ です。

__多値従属__ とは、例えば、K1,K2,K3の3列からなるテーブルについて、K1が決まるとK2がどの値をとってもK3に出現する値の組合せが決まること、と定義できます。  

例えば、例で挙げたテーブルを見ると、チームがAに決まると、社員がどの値をとっても、顧客の値は「〇〇株式会社、××株式会社、△△株式会社」の組合せになります。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/2333eb31-da95-a969-d5ab-e2440a5c8ff8.png)

以上のように、チームが、顧客の取りうる値の組合せを、社員に依存せず決定するのが、多値従属です。

ちなみにこの時、チーム・顧客でデータを並べ替えてみると、チームは社員も多値従属させることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f1b1ec13-7537-26cb-dbd6-47e454aac75d.png)
一般に、K1,K2,K3の3列からなるテーブルについて、K1がK3を多値従属させるとき、K1はK2も多値従属させます。例えば、チームがAに決まると、Aに所属する全社員に対して、Aの担当する全顧客のレコードが出てくるので、逆もまた同様になるからです。

そして、これを元の2テーブルに分解して解消するのが、第4正規形(第4正規化)です。

チーム所属社員マスタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7b28a8f5-fb5c-f484-043b-bed7b8f99136.png)

チーム担当顧客マスタ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/e3ea787c-8476-5134-3e92-89ba7483259f.png)


## 4-4. 補足
参考書では、例えばK1、K2、K3の3列からなるテーブルに対して、K1がK3を多値従属させることを、K2を考慮せずに決定できるように記載しています。が、この記事で「K1が決まると __K2がどの値をとっても__ K3に出現する値の組合せが決まること」と定義したように、実際にはK2に対する評価が必要です。
参考書には、不正確な説明が複数存在しますが、大体は、分かりやすさを優先するためにごまかしたんだなぁ、と理解できるものです。ですが、この部分は、さすがにやりすぎな気がします。









# 5. 第5正規形
コンセプトは第4正規形と同じで、評価する内容が若干違います。こちらも、主キーのみのテーブルに対して評価します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/bee0c3c0-da2f-fdf5-6e25-0ca3f679b345.png)
## 5-1. 第5正規形のイメージ
第5正規形(第5正規化)のイメージも __「単純掛け合わせテーブルを作るな」__ です。

### 単純掛け合わせテーブルを作るな
通常、正規化は、非正規形のテーブルを正規化する順で説明しますが、第5正規形も逆順の方が分かりやすいです。  

例えば、以下のように、チームと担当顧客、チームと所属する社員が決まっているとします。ここまでは第4正規形の時と同じです。

チーム所属社員マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/fc222430-96d2-598c-04e6-16ab572215f6.png)

チーム担当顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/df458d20-23ef-cc19-35e9-8751d54b4624.png)

ここでさらに、以下のように、各社員が担当可能な顧客が決まっているとします。

社員担当顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/7791ff04-b855-0649-da43-9ec33ce0e7be.png)

この時、これらのテーブルを単純に掛け合わせた[^501]、以下のようなテーブルを考えることができます。

チーム社員顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b676efa1-533c-3b00-67dd-16ca06c971df.png)

[^501]: 3テーブルを、同じ列が一致すること条件に、以下のSQLで結合するイメージです。
select T1.チーム, T1.社員, T2.顧客   
from チーム所属社員マスタ T1, チーム担当顧客マスタ T2, 社員担当可能顧客マスタ T3   
where T1.チーム = T2.チーム and T2.顧客 = T3.顧客 and T3.社員 = T1.社員 

第4正規形の時と同様に、このテーブルでも、例えば「後藤さん」が「チームA」に所属していることは分かります。
さらに、このテーブルは多値従属がなく、第4正規形です。(チームがAに決まっても、社員が後藤さん佐々木さんかによって、顧客が取りうる値の組合せが変わるので)
では、このテーブルでチームと社員と顧客の対応を管理していいのか。__ダメ。元の3テーブルに分解して管理しなさい__、というのが第5正規形です。

## 5-2. なぜ第5正規形が必要なのか
第5正規形に分解していないと、矛盾したデータを作れてしまうからです。

例えば、佐々木さんが△△株式会社も担当可能になったとして、うっかり以下のようにレコードを足してしまうと、チームBも△△株式会社を担当できることになってしまいます。  

チーム社員顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/3e5e47aa-692a-5a91-8c64-b0de47e2da50.png)


テーブルが分解されていれば、佐々木さんと顧客のレコードは社員担当顧客マスタに追加するだけなので、チームに影響が及びません。

社員担当顧客マスタ  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/b9804c9d-5e0c-218c-4b08-88dc2e4bbb5b.png)


## 5-3. もう少し正確な説明
第5正規形の定義は、主キーのみのテーブルに対して __「結合従属が解消されていること」__ です。

__結合従属__ とは、あるテーブルが、複数の別テーブルの単純掛け合わせになっていることです。第5正規形については、イメージの説明がそのまま定義として使えます。そしてこれを元の複数テーブルに分解して解消するのが、第5正規化です。

## 5-4. 補足
第5正規化の定義が「単純掛け合わせテーブルを元のテーブルに分解すること」なら、第4正規化と何が違うの？と思った方がいるかと思います。  
これはその通りで、第5正規化というのは、以下のように第4正規化の一般化であり、本質的にやっていることは同じです。
・第4正規化：多値従属という特殊な前提を満たす掛け合わせテーブルの分解
・第5正規化：前提を問わず掛け合わせテーブルならとにかく分解

さらに言うなら、実は、第2正規化、第3正規化、ボイス-コッド正規化も、関数従属という特殊な前提を満たすテーブルの分解であり、分解後のテーブルを単純掛け合わせすると、もとのテーブルに戻ります。つまり、 __結合従属の解消は正規化の概念そのもの__ と捉えることもできるのです。





# 6. 第6正規形
キー列・非キー列を問わず、全体の結合従属性を解消します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/4bf9f73a-d3d7-4f68-8848-3f90b50beb04.png)


## 6-1 第6正規形とは
第6正規形は実務で使わないので、ざっくり行かせてください。

第6正規形の定義は、__全ての結合従属性が解消されている__ ことです。

まず、第5正規形のテーブルは第6正規形でもあります。
主キーのみのテーブルに限定されてはいますが、結合従属性は解消されているからです。

では、非キー列を含むテーブルを第6正規化するにはどうしたらよいか。
ボイス-コッド正規化まで行ったテーブルを以下のように、非キー列ごとに __非キー列を1列しか持たないテーブルへ分解__ します。

○第6正規化前

注文明細
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/85a3b06d-be4d-9c82-0b3d-a01482e5cc72.png)


○第6正規化後

注文明細_商品
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/a4f7dc12-8c0d-39ca-c3bd-06e725386298.png)

注文明細_数量
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/944269/f89861e2-40e2-1d3a-22c7-6c58e5f3fe9b.png)

このように、非キー列を1列1列分解しても、単純掛け合わせでもとのテーブルには戻りますので、これも結合従属性の解消ではあるわけです。

正直、あまり意味のない分解ではあるのですが、正規化の概念そのものともいえる結合従属性の解消を、最後まで推し進めるとこうなります、というのが第6正規形です。




# 最後に
ということで、正規化の説明でした。  

頭の中では、もっとシンプルにまとまっていたはずなのですが、実際アウトプットしてみたら、とんでもなく長くなったうえに、そもそも分かりやすい説明になったのかも自信がなくなってしまいました…。

この記事が少しでも、お役に立てばよいのですが…。