はじめに
データベースの正規化について。
「第1正規形」は繰り返しをなくすだけなので、直感的に「あ、これまだ第1正規化されてないな」とわかります。
でも、「第2正規形と第3正規形については、このテーブルは第2正規化できているんだっけ?部分従属?推移従属??」と、個人的によく迷子になります。
今回はその悩みを解決するための記事です。
そもそも正規化とは?
データの重複をなくし整合的にデータを取り扱えるようにデータベースを設計することを、データベースの正規化と呼びます。
OSS-DB道場より引用
要は、「データの重複をなくして、スッキリさせようぜ!」ということ。
関係データベースでは「1つの表には1つの事実だけがある」という規則に従って表を設計します。
社員テーブルには社員に関するデータ(社員ID、名前、所属部署IDなど)だけを、部署テーブルには部署に関するデータ(部署ID、部署名、所在地など)だけを置く、というルールです。
正規化のステップ
第一正規化から第三正規化についてそれぞれ説明していきたいと思います。
第1正規形:繰り返しの排除(詰め込みすぎ状態を排除)
1つのセルに複数の値が存在する状態、または同じ意味を持つ列が複数存在する状態(繰り返し項目)を排除し、すべての属性が単一の値を持つようにすること。
【具体的には】
第1正規化ができていないテーブルには、よくある「2つのダメパターン」があります。これを解消して、縦にスッキリ並べ直します。
ダメなパターン①:1つのマスに複数詰め込む
| 注文ID | 注文日 | 注文商品 |
|---|---|---|
| 1 | 10/01 | りんご, みかん |
ダメなパターン②:同じ意味のカラムを横に増やす
| 注文ID | 注文日 | 商品1 | 商品2 | 商品3 |
|---|---|---|---|---|
| 1 | 10/01 | りんご | みかん | (空欄) |
▼ 正規化後(1マスに1データ & 縦に並べる)
| 注文ID | 注文日 | 注文商品 |
|---|---|---|
| 1 | 10/01 | りんご |
| 1 | 10/01 | みかん |
【要はどういうことか】
-
真面目に言うと:
同じ意味を持つデータを、1つのマスに複数入れたり、横に列を増やして並べたりせず、1行1データとして独立させること。 -
ざっくり言うと:
「1つのマスに複数詰め込むな!あと『商品1』『商品2』みたいに同じ役割の項目を横にダラダラ並べるな!縦にピシッと整列しろ!」
第2正規形:部分従属の排除(相方無視状態を排除)
第1正規形を満たした上で、複合主キーの一部にのみ従属している属性(部分関数従属性)を別の表に切り出すこと。
【具体的には】
主キーが「注文ID」と「商品ID」の2つ合わさって機能しているテーブルにおいて、「商品名」は「商品ID」さえ分かれば特定できてしまいます。これを切り出します。
▼ 正規化前(商品名が商品IDにのみ従属している)
| 注文ID(主キー) | 商品ID(主キー) | 注文数量 | 商品名 |
|---|---|---|---|
| 1 | A01 | 2 | りんご |
| 1 | B02 | 3 | みかん |
| 2 | A01 | 1 | りんご |
▼ 正規化後(中途半端に従っている項目を別テーブルへ)
注文明細テーブル
| 注文ID(主キー) | 商品ID(主キー) | 注文数量 |
|---|---|---|
| 1 | A01 | 2 |
| 1 | B02 | 3 |
| 2 | A01 | 1 |
商品テーブル
| 商品ID(主キー) | 商品名 |
|---|---|
| A01 | りんご |
| B02 | みかん |
【要はどういうことか】
-
真面目に言うと:
複合主キー(複数の項目が合わさって主キーになるもの)の「一部」にだけ依存しているデータを、別テーブルに切り分けること。 -
ざっくり言うと:
主キーが「注文ID」と「商品ID」のコンビで成立しているのに、片方の「商品ID」にだけ従っている項目(商品名)がいる状態。「コンビなんだから相方無視して片方だけに従うな!」と、そいつを別テーブルに独立させるのが第2正規形。 -
備考
「相方無視」が起きるのは、主キーが2つ以上のコンビ(複合主キー)の時だけ。
つまり、主キーが1つしかないテーブルは、第1正規化が終わった時点で自動的に第2正規形もクリアしていることになる。
第3正規形:推移従属の排除(裏ボスがいる状態を排除)
第2正規形を満たした上で、主キー以外の属性に従属している属性(推移的関数従属性)を別の表に切り出すこと。
【具体的には】
主キーは「社員ID」なのに、「部署名」は「部署ID」によって決まっています(社員ID → 部署ID → 部署名という推移状態)。この孫受けのような関係を切り出します。
▼ 正規化前(部署名が部署IDに従属している)
| 社員ID(主キー) | 社員名 | 部署ID | 部署名 |
|---|---|---|---|
| 101 | 田中 | D01 | 営業部 |
| 102 | 鈴木 | D01 | 営業部 |
| 103 | 佐藤 | D02 | 開発部 |
▼ 正規化後(隠れボスとその派閥を別テーブルへ)
社員テーブル
| 社員ID(主キー) | 社員名 | 部署ID |
|---|---|---|
| 101 | 田中 | D01 |
| 102 | 鈴木 | D01 |
| 103 | 佐藤 | D02 |
部署テーブル
| 部署ID(主キー) | 部署名 |
|---|---|
| D01 | 営業部 |
| D02 | 開発部 |
【要はどういうことか】
-
真面目に言うと:
主キーではない別の項目(例:部署ID)に依存している「孫データ(例:部署名)」を、別テーブルに独立させること。 -
ざっくり言うと:
主キー(社員ID)が絶対的リーダーのはずなのに、サブのヤツ(部署ID)が裏で他の項目(部署名)を牛耳っている状態。「リーダーを通さずに勝手に裏で派閥を作るな!」と、その裏ボスと派閥を別テーブルに独立させるのが第3正規形。
まとめ
データベースの正規化をする際は、「誰が誰に従っているか」ということを意識するようにしています。
- 第2正規形になっていないテーブル = テーブルの中に「相方無視」が起こっている状態
- 第3正規形になっていないテーブル = テーブルの中に「裏ボス」がいる状態
僕はこう覚えるようにしています!
🍌NanoBanana君による解説漫画