テーブルの正規化 ~テーブルを分割する必要性~

正規化とは何か

【エンジニア募集中】フルリモート可◎、売上/従業員数9年連続UP、平均残業8時間、有給取得率90%、年休124日以上 etc.  詳細はこちらから>

正規化とは、リレーショナルデータベースに矛盾したデータを保持できないようにテーブルを分割することです。

テーブルを適当に作ると色々困ることが出てきます。1つの情報が複数のテーブルに存在して無駄なデータ領域と面倒な更新処理を発生させてしまったり、更新処理のタイムラグによってデータの不整合が発生したり、そもそもデータを登録することができないようなテーブルを作ってしまうことがあります。

これらの問題を解決する方法が正規化になります。正規化は一般的に第1~5正規形がよく知られていますが、通常の業務では第3正規形まで実施することが多いです。

第1正規形

第1正規形の定義は「1つのセルの中には1つの値しか含まない」です。

下記の社員テーブルは、社員ID:00003の部署IDと部署名に2つの値が入っているため、第1正規形を満たしていません。このような場合はテーブルを分割して第1正規形にする必要があります。

・社員テーブル

社員ID

社員名

年齢

部署ID

部署名

00001

田中

30

A01

開発

00002

鈴木

28

A02

人事

00003

山本

41

A02

A03

人事

総務

・社員テーブル(第1正規化後)

社員ID

社員名

年齢

00001

田中

30

00002

鈴木

28

00003

山本

41

・社員明細テーブル(第1正規化後)

社員ID

部署ID

部署名

00001

A01

開発

00002

A02

人事

00003

A02

人事

00003

A03

総務

このように複数の値が入っている列を別テーブルに分割することで、第1正規形にすることができました。

なぜ1つのセルに複数の値を入れてはいけないかというと、主キーが各列の値を一意に決定できないからです。1つのセルに1つの値であれば、主キーは各列の値を一意に決めることができます。この概念を関数従属性と呼び、「ある列Xの値が決まれば、ある列Yの値も決まる」というもので、例えば正規化後の社員テーブルだと、社員IDが決まれば社員名が決まりますし、年齢も決まります。一方、正規化前の社員テーブルでは社員IDから部署IDと部署名は一意になりません。

第2正規形 部分関数従属

第2正規化で行うことは部分関数従属を解消し、完全関数従属のテーブルにしていくことです。

部分関数従属とはテーブルに主キーが複数ある場合に、主キーの一部に対して従属している非主キー列が存在する状態です。これに対して完全関数従属は、非主キー列が複数の主キー全体に関数従属している状態です。下記のテーブルで確認します。

・社員テーブル

会社コード

会社名

社員ID

社員名

年齢

部署コード

部署名

C001

smallit

00001

田中

30

A01

開発

C001

smallit

00002

鈴木

28

A02

人事

C002

千種製菓

00001

戸田

65

A03

総務

C002

千種製菓

00002

大本

29

A01

開発

このテーブルの主キーは会社コードと社員IDです。したがって非主キー列はこの主キーに従属する必要がありますが、会社名は主キーの一部である会社コードにのみ従属しています(社員IDが変わろうが、会社名は変わらない)。

これを解消して完全関数従属にしていきます。部分関数従属の解消の仕方もテーブルの分割です。次のように部分関数従属の関係にあるキー列と従属列を独立のテーブルにします。

・社員テーブル(第2正規化後)

会社コード

社員ID

社員名

年齢

部署コード

部署名

C001

00001

田中

30

A01

開発

C001

00002

鈴木

28

A02

人事

C002

00001

戸田

65

A03

総務

C002

00002

大本

29

A01

開発

・会社テーブル(第2正規化後)

会社コード

会社名

C001

smallit

C002

千種製菓

こうすることで、社員テーブルも会社テーブルも全ての非主キー列が主キーに完全関数従属するようになりました。

ではなぜ第2正規形にする必要があるのでしょうか。第2正規化をしないと起こる問題が2つあり、1つ目は正規化前の社員テーブルには社員IDが主キーに含まれているため、社員のいない会社を登録することができません。

2つ目は会社コードと会社名の間違った組み合わせを登録できてしまうことです。例えば{会社コード:C001、会社名:smallit} 以外の組み合わせである{会社コード:C001、会社名:山田商事}も簡単に登録できてしまいます。

第2正規化をすることで1つ目の問題は会社テーブルに新しく{会社コード:C003、会社名:山田商事}と登録することができますし、2つ目の問題も会社テーブルで会社コードと会社名の組み合わせを管理しているので解決できます。

第3正規形 推移的関数従属

第3正規化では推移的関数従属を解消していきます。

第2正規化後の社員テーブルを見てみると、明らかに{部署コード}→{部署名}の関数従属が存在します。

また、主キーである会社コード、社員IDと部署コードの間にも{会社コード、社員ID}→{部署コード}となる関数従属が存在します。

全体として、{会社コード、社員ID}→{部署コード}→{部署名}という2段階の関数従属が存在しています。このような関数従属を推移的関数従属と呼びます。

推移的関数従属の何が問題かというと、部署に新しく「品質管理」を作成したい場合、社員IDが主キーであるために、所属する社員がいないと品質管理は作成ができません。立ち上げたばかりの部署で所属の社員が0だったりすることはあるわけで、こういった場合に不都合が発生します。

推移的関数従属の解決方法はこれまでと同じように、テーブルを分割することでそれぞれの関数従属の関係を独立させます。

・社員テーブル

会社コード

社員ID

社員名

年齢

部署コード

C001

00001

田中

30

A01

C001

00002

鈴木

28

A02

C002

00001

戸田

65

A03

C002

00002

大本

29

A01

・会社テーブル

会社コード

会社名

C001

smallit

C002

千種製菓

・部署テーブル

部署コード

部署名

A01

開発

A02

人事

A03

総務

A04

品質管理

このように部署テーブルを独立することで全てのテーブルは非主キー列が主キー列に対してのみ従属するようになり、推移的関数従属は解消されました。

【エンジニア募集中】フルリモートも◎(リモート率85.7%)、平均残業8時間、年休124日以上、有給取得率90% etc. 詳細はこちらから>

Smallitのサービス