- 開発技術
テーブルの正規化 ~テーブルを分割する必要性~
- SQL
正規化とは何か
【エンジニア募集中】フルリモート可◎、売上/従業員数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. 詳細はこちらから>