【テーブル正規化】候補キーから第3正規化までをマスターする

この記事は移行しました！[最新の内容はこちら](https://naoqoo2.github.io/blog/%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E6%AD%A3%E8%A6%8F%E5%8C%96-%E5%80%99%E8%A3%9C%E3%82%AD%E3%83%BC%E3%81%8B%E3%82%89%E7%AC%AC3%E6%AD%A3%E8%A6%8F%E5%8C%96%E3%81%BE%E3%81%A7%E3%82%92%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%99%E3%82%8B/)をご覧ください😀

## はじめに

今週末はデータベーススペシャリスト試験ですね。
DBの正規化について、毎回忘れて勉強し直すのでまとめておこうと思います。

試験でも実務でも第３正規化まで出来れば大概OKですね。
下記順序で進めるのがポイントです。

0. 非正規形
1. 第1正規形にする
2. 候補キーを探す　←ここがミソ！
3. 主キーを探す　←ここもミソ！
4. 第2正規形にする
5. 第3正規形にする

Qiitaなので記事テーブルを例に考えてみましょう。

記事テーブル（記事ID, 記事タイトル, カテゴリID, カテゴリ名, カテゴリ内での表示順, 作成者ユーザID, コメント番号, コメント本文, コメント投稿者ユーザID）

参考:[2017年度春期試験](https://www.jitec.ipa.go.jp/1_04hanni_sukiru/mondai_kaitou_2017h29.html#29haru)の午後Ⅰ
> 電子会議投稿（電子会議番号, 議題, 分野番号, 分野名, 表示順, 作成者ユーザID, 投稿番号, 投稿本文, 投稿者ユーザID）


## 非正規形

人間の目でパッと見てわかりやすいのが非正規形です。
例では2つの記事がありますが、行の中に複数の行（ここではコメント）がある状態です。
これを『繰り返し項目が存在する』と言います。この状態が非正規形です。
テーブルを作れない状態と思っていいでしょう。

<table>
<caption>記事</caption>
<thead>
<th>記事ID</th><th>記事タイトル</th><th>カテゴリID</th><th>カテゴリ名</th><th>カテゴリ内での表示順</th><th>作成者ユーザID</th><th>コメント番号</th><th>コメント本文</th><th>コメント投稿者ユーザID</th>
</thead>
<tbody>
<tr>
    <td rowspan="3">11111</td><td rowspan="3">候補キーと第3正規化までを理解する</td>
    <td rowspan="3">cate1</td><td rowspan="3">データベース</td><td rowspan="3">99</td>
    <td rowspan="3">naoqoo2</td>
    <td>1</td><td>いい記事です</td><td>xxxx</td>
</tr>
<tr>
    <td>2</td><td>とてもわかりやすかったです</td><td>yyyy</td>
</tr>
<tr>
    <td>3</td><td>大変勉強になりました</td><td>zzzz</td>
</tr>
</tbody>
<tbody>
<tr>
    <td>22222</td><td>主キーの種類</td>
    <td>cate1</td><td>データベース</td><td>98</td>
    <td>naoqoo2</td>
    <td>1</td><td>ステキな記事です</td><td>xxxx</td>
</tr>
</tbody>
</table>


## 第1正規形にする

データが重複しているイケてないテーブルが第1正規形です。
非正規形から繰り返し項目をなくす（1行にしていく）と第1正規形になります。
例だと2→4レコードになりました。
これならテーブルが作れますね！でもイケてない。

<table>
<caption>記事テーブル</caption>
<thead>
<th>記事ID</th><th>記事タイトル</th><th>カテゴリID</th><th>カテゴリ名</th><th>カテゴリ内での表示順</th><th>作成者ユーザID</th><th>コメント番号</th><th>コメント本文</th><th>コメント投稿者ユーザID</th>
</thead>
<tbody>
<tr>
    <td>11111</td><td>候補キーと第3正規化までを理解する</td>
    <td>cate1</td><td>データベース</td><td>99</td>
    <td>naoqoo2</td>
    <td>1</td><td>いい記事です</td><td>xxxx</td>
</tr>
<tr>
    <td>11111</td><td>候補キーと第3正規化までを理解する</td>
    <td>cate1</td><td>データベース</td><td>99</td>
    <td>naoqoo2</td>
    <td>2</td><td>とてもわかりやすかったです</td><td>yyyy</td>
</tr>
<tr>
    <td>11111</td><td>候補キーと第3正規化までを理解する</td>
    <td>cate1</td><td>データベース</td><td>99</td>
    <td>naoqoo2</td>
    <td>3</td><td>大変勉強になりました</td><td>zzzz</td>
</tr>
<tr>
    <td>22222</td><td>主キーの種類</td>
    <td>cate1</td><td>データベース</td><td>98</td>
    <td>naoqoo2</td>
    <td>1</td><td>ステキな記事です</td><td>xxxx</td>
</tr>
</tbody>
</table>

## 候補キーを探す

次に主キーを決めたいのですが、主キーの前に候補キーを探す必要があります。
候補キーとは主キーの候補になるキーのことです。そのままですね。
テーブルの中で１つのレコード（ユニーク）を特定できるカラム。またはカラムの組み合わせが候補キーです。


- 『記事ID』と『コメント番号』

でレコードを特定できますね。
また、記事IDの代わりにカテゴリIDと表示順を使用してもレコードを特定できそうです。

- 『カテゴリID』と『カテゴリ内での表示順』と『コメント番号』

この2つが候補キーとなります。

## 主キーを探す

開発者が候補キーからただ一つ選択したキーが主キーです。
NULLは許されないという制約があります。

今回は2つの候補キーからどちらかを選ぶことになります。

- 記事ID, コメント番号
- カテゴリID, カテゴリ内での表示順, コメント番号

記事を投稿すれば必ず記事IDは入りそうですが、
カテゴリや表示順は決まっていない（NULL）可能性がありますよね？
そのためここでの主キーは

- 記事ID, コメント番号

となります。

## 第2正規形にする

主キーを構成する一部のカラムによって決まるもの（部分関数従属）を別テーブルにすると第2正規形となります。

例での部分関数従属は下記のようになります。

記事IDから決まるもの

- 記事タイトル
- カテゴリID
- カテゴリ名
- カテゴリ内での表示順
- 作成者ユーザID

コメント番号から決まるもの

- なし（コメント番号は記事に対して連番なのでコメント本文などを特定できない）

よって下記のようになります。

<table>
<caption>記事テーブル</caption>
<thead>
<th>記事ID</th><th>記事タイトル</th><th>カテゴリID</th><th>カテゴリ名</th><th>カテゴリ内での表示順</th><th>作成者ユーザID</th>
</thead>
<tbody>
<tr>
    <td>11111</td><td>候補キーと第3正規化までを理解する</td>
    <td>cate1</td><td>データベース</td><td>99</td>
    <td>naoqoo2</td>
</tr>
<tr>
    <td>22222</td><td>主キーの種類</td>
    <td>cate1</td><td>データベース</td><td>98</td>
    <td>naoqoo2</td>
</tr>
</tbody>
</table>

<table>
<caption>コメントテーブル</caption>
<thead>
<th>記事ID</th><th>コメント番号</th><th>コメント本文</th><th>コメント投稿者ユーザID</th>
</thead>
<tbody>
<tr>
    <td>11111</td>
    <td>1</td><td>いい記事です</td><td>xxxx</td>
</tr>
<tr>
    <td>11111</td>
    <td>2</td><td>とてもわかりやすかったです</td><td>yyyy</td>
</tr>
<tr>
    <td>11111</td>
    <td>3</td><td>大変勉強になりました</td><td>zzzz</td>
</tr>
<tr>
    <td>22222</td>
    <td>1</td><td>ステキな記事です</td><td>xxxx</td>
</tr>
</tbody>
</table>

## 第3正規形にする

最後に第3正規形です。
主キー以外のカラム（非キー）によって決まるもの（推移的関数従属）を別テーブルにすると第3正規形となります。

例ではカテゴリ名がカテゴリIDによって決まるので推移的関数従属ですね。

記事ID→カテゴリID→カテゴリ名

テーブルは下記のようになります。

<table>
<caption>記事テーブル</caption>
<thead>
<th>記事ID</th><th>記事タイトル</th><th>カテゴリID</th><th>カテゴリ内での表示順</th><th>作成者ユーザID</th>
</thead>
<tbody>
<tr>
    <td>11111</td><td>候補キーと第3正規化までを理解する</td>
    <td>cate1</td><td>99</td>
    <td>naoqoo2</td>
</tr>
<tr>
    <td>22222</td><td>主キーの種類</td>
    <td>cate1</td><td>98</td>
    <td>naoqoo2</td>
</tr>
</tbody>
</table>

<table>
<caption>カテゴリテーブル</caption>
<thead>
<th>カテゴリID</th><th>カテゴリ名</th>
</thead>
<tbody>
<tr>
    <td>cate1</td><td>データベース</td>
</tr>
</tbody>
</table>

<table>
<caption>コメントテーブル</caption>
<thead>
<th>記事ID</th><th>コメント番号</th><th>コメント本文</th><th>コメント投稿者ユーザID</th>
</thead>
<tbody>
<tr>
    <td>11111</td>
    <td>1</td><td>いい記事です</td><td>xxxx</td>
</tr>
<tr>
    <td>11111</td>
    <td>2</td><td>とてもわかりやすかったです</td><td>yyyy</td>
</tr>
<tr>
    <td>11111</td>
    <td>3</td><td>大変勉強になりました</td><td>zzzz</td>
</tr>
<tr>
    <td>22222</td>
    <td>1</td><td>ステキな記事です</td><td>xxxx</td>
</tr>
</tbody>
</table>

これで第３正規化までできました！

## おまけ：主キーの種類
### ナチュラルキー(自然キー, natural key)
主キーが、ユーザーにとって意味のあるカラム。またはカラムの組み合わせ。

### サロゲートキー(代替キー、surrogate key)
主キーが、連番などのユニーク性を確保するためだけに用意した（ユーザーにとって意味のない）カラム。
そのため複合キー（カラムの組み合わせ。）となることはない。（よほど変な設計しない限り）

※ちなみに代理キーと呼ばれることもあるが、本来代理キーは候補キーのうち主キーとして選ばれなかったキーのことを言うらしい。まぁサロゲートキーって呼ぼう。
[本来の代理キー](https://ja.wikipedia.org/wiki/%E4%BB%A3%E7%90%86%E3%82%AD%E3%83%BC)

## 参考サイト

- [データベースの正規化(正規形)とはなんぞや](http://gomocool.net/gomokulog/?p=514)
- [第4回　データベースの正規化](http://www.oss-db.jp/measures/dojo_info_04.shtml)
- [候補キーと主キーの違い](https://nwdb.exblog.jp/862558/)