データベースの正規化~第1正規形から第5正規形まで

2025/03/19

データ

正規化とは

データベース設計において、データの重複をなくし、一貫性と整合性を保ちながら効率的に情報を管理できるように、テーブルを整理・分割することを正規化と呼ぶ。正規化により、同じ情報が複数の場所に保存されることを防ぎ、情報を変更する際には1箇所だけデータを更新すれば良くなるため、更新漏れや不整合の発生を予防できる。

正規化には、第1正規形から第5正規形までの段階があり、段階が進むごとにテーブルは細分化されていく。各段階の正規化では、正規化前の状態に戻せるようにテーブルを分解する(無損失分解)。データベースが高度に正規化されている場合、求める情報を得るために多くのテーブルを結合する必要があり、パフォーマンスに影響を与えることもある。そのため、実務では原則として第3正規形までの正規化が推奨されるが、場合によってはパフォーマンス向上のためにあえて非正規化することもある。

以下、具体例をもとに各正規化について解説する。

非正規形

まずは、正規化を行う前の非正規形の例を示す。以下、この表をもとに正規化していく。

領収書ID購入時間購入店舗商品名顧客ID顧客名サイズカラーキャンペーン単価個数購入価格責任者
R0012023/04/10 09:00店舗ATシャツC001山田太郎M学生向けキャンペーン250025000田中一郎
同上同上同上ジーンズ同上同上L学生向けキャンペーン600016000同上
R0022023/04/11 14:30店舗BワンピースC002佐藤花子S新春キャンペーン900019000山本花子
R0032023/04/14 10:00店舗ATシャツC003高橋三郎M在庫一掃キャンペーン260012600鈴木太郎
R0042023/04/15 11:00店舗ATシャツC004伊藤四郎S学生向けキャンペーン250012500佐藤次郎

第1正規形

第1正規形は一つのセルに一つの値のみを持っている状態。上述の非正規形を第1正規形に正規化したものを以下に示す。

領収書ID (pk)購入時間 (pk)購入店舗 (pk)商品名 (pk)顧客ID顧客名サイズカラーキャンペーン単価個数購入価格責任者
R0012023/04/10 09:00店舗ATシャツC001山田太郎M学生向けキャンペーン250025000田中一郎
R0012023/04/10 09:00店舗AジーンズC001山田太郎L学生向けキャンペーン600016000田中一郎
R0022023/04/11 14:30店舗BワンピースC002佐藤花子S新春キャンペーン900019000山本花子
R0032023/04/14 10:00店舗ATシャツC003高橋三郎M在庫一掃キャンペーン260012600鈴木太郎
R0042023/04/15 11:00店舗ATシャツC004伊藤四郎S学生向けキャンペーン250012500佐藤次郎

※ pk(primary key:主キー)はレコードを一意に識別するために利用するためのカラムのこと。上の例では{領収書ID, 購入時間, 購入店舗, 商品名}の組(複合主キー)が決まれば、どのレコードを指しているか特定できる。

第2正規形

第2正規形は、第1正規形の条件に加えて、部分関数従属を排除した状態。部分関数従属とは、複合主キーの一部によって他の項目が一意に定まること。

上述の第1正規形では、「顧客ID」、「顧客名」、「責任者」は、複合主キー({領収書ID, 購入時間, 購入店舗, 商品名}の組)ではなく、複合主キーの一部({領収書ID, 購入時間, 購入店舗}の組)によって一意に定まる。つまり、次のような部分関数従属がある。

{領収書ID, 購入時間, 購入店舗} → {顧客ID}
{領収書ID, 購入時間, 購入店舗} → {顧客名}
{領収書ID, 購入時間, 購入店舗} → {責任者}

※ A → Bは、BがAに関数従属していることを表し、Aが決まるとBが決まる。

そこで、この部分関数従属を排除するため、次のように分割する。

(1) 購入マスタ

領収書ID (pk)購入時間 (pk)購入店舗 (pk)顧客ID顧客名責任者
R0012023/04/10 09:00店舗AC001山田太郎田中一郎
R0022023/04/11 14:30店舗BC002佐藤花子山本花子
R0032023/04/14 10:00店舗AC003高橋三郎鈴木太郎
R0042023/04/15 11:00店舗AC004伊藤四郎佐藤次郎

(2) 購入詳細

領収書ID (pk)購入時間 (pk)購入店舗 (pk)商品名 (pk)キャンペーンサイズカラー単価個数購入価格
R0012023/04/10 09:00店舗ATシャツ学生向けキャンペーンM250025000
R0012023/04/10 09:00店舗Aジーンズ学生向けキャンペーンL600016000
R0022023/04/11 14:30店舗Bワンピース新春キャンペーンS900019000
R0032023/04/14 10:00店舗ATシャツ在庫一掃キャンペーンM260012600
R0042023/04/15 11:00店舗ATシャツ学生向けキャンペーンS250012500

第3正規形

第3正規形は、第2正規形の条件に加えて、推移的関数従属を排除した状態。推移的関数従属とは、二段階以上の従属関係があること。

上述の第2正規形の購入マスタでは、「顧客名」は「顧客ID」に従属していて、「顧客ID」は複合主キーに従属している。つまり、次のような推移的関数従属がある。

{領収書ID, 購入時間, 購入店舗} → {顧客ID} → {顧客名}

※ A → Bは、BがAに関数従属していることを表し、Aが決まるとBが決まる。

そこで、この推移的関数従属を排除するため、購入マスタを次のように分割する。

(1) 顧客マスタ

顧客ID (pk)顧客名
C001山田太郎
C002佐藤花子
C003高橋三郎
C004伊藤四郎

(2) 購入マスタ(更新版)

領収書ID (pk)購入時間 (pk)購入店舗 (pk)顧客ID責任者
R0012023/04/10 09:00店舗AC001田中一郎
R0022023/04/11 14:30店舗BC002山本花子
R0032023/04/14 10:00店舗AC003鈴木太郎
R0042023/04/15 11:00店舗AC004佐藤次郎

ボイス・コッド正規形

ボイス・コッド正規形は、第3正規形よりさらに厳しく、テーブル内の非自明なすべての従属関係が、レコードを一意に識別できるキー(スーパーキー)への従属になっている状態。第3.5正規形と呼ばれることもある。

「責任者は複数の店舗を兼任できない」というルールが存在すると仮定する。つまり、上述の第3正規形の購入マスタでは、「購入店舗」は「責任者」(非キー)に従属していることになる。

{責任者} → {購入店舗}

※ A → Bは、BがAに関数従属していることを表し、Aが決まるとBが決まる。

そこで、この非キーへの関数従属を排除するため、購入マスタを次のように分割する。

(1) 購入マスタ(更新版)

領収書ID (pk)購入時間 (pk)顧客ID責任者 (pk)
R0012023/04/10 09:00C001田中一郎
R0022023/04/11 14:30C002山本花子
R0032023/04/14 10:00C003鈴木太郎
R0042023/04/15 11:00C004佐藤次郎

(2) 責任者マスタ

責任者 (pk)購入店舗
田中一郎店舗A
山本花子店舗B
鈴木太郎店舗A
佐藤次郎店舗A

第4正規形

第4正規形は、ボイス・コッド正規形の条件に加えて、複数の多値従属を排除した状態。多値従属とはある項目が決まると他の項目の集合が決まること。

次に示す商品マスタにおいて、「商品名」が決まれば「サイズ」と「カラー」の集合が決まる。例えば、商品名がTシャツであれば、サイズはS/L/Mのいずれか、カラーは赤/青/白のいずれかであると決まる。このとき、「サイズ」と「カラー」はそれぞれ独立で決定する。

商品マスタ

商品名 (pk)サイズ (pk)カラー (pk)
TシャツS
TシャツS
TシャツS
TシャツM
TシャツM
TシャツM
TシャツL
TシャツL
TシャツL
ジーンズS
ジーンズS
ジーンズM
ジーンズM
ジーンズL
ジーンズL
ジーンズXL
ジーンズXL
ワンピースS
ワンピースS
ワンピースM
ワンピースM
ワンピースL
ワンピースL

つまり、次のような2つの多値従属がある。

{商品名} →→ {サイズ}
{商品名} →→ {カラー}

※ A →→ Bは、BがAに多値従属していることを表し、Aが決まるとBの集合が決まる。

そこで、この2つの多値従属を排除するため、商品マスタを次のように分割する。

(1) 商品名_サイズ

商品名 (pk)サイズ (pk)
TシャツS
TシャツM
TシャツL
ジーンズS
ジーンズM
ジーンズL
ジーンズXL
ワンピースS
ワンピースM
ワンピースL

(2) 商品名_カラー

商品名 (pk)カラー (pk)
Tシャツ
Tシャツ
Tシャツ
ジーンズ
ジーンズ
ワンピース
ワンピース

第5正規形

第5正規形は、第4正規形の条件に加えて、結合従属を排除した状態。結合従属とは3つ以上に分解可能な従属性のこと。

「店舗ごとに取扱商品と実施されるキャンペーンは異なり、商品ごとに適用可能なキャンペーンも異なる」というルールが存在すると仮定する。そうすると、次に示すキャンペーンマスタにおいて、「購入店舗」が決まれば「商品名」と「キャンペーン」の集合が決まり、「商品名」が決まれば「キャンペーン」の集合が決まる。

キャンペーンマスタ

購入店舗 (pk)商品名 (pk)キャンペーン (pk)
店舗ATシャツ学生向けキャンペーン
店舗ATシャツ在庫一掃キャンペーン
店舗Aジーンズ学生向けキャンペーン
店舗BTシャツ新春キャンペーン
店舗BTシャツ在庫一掃キャンペーン
店舗Bジーンズ新春キャンペーン
店舗Bワンピース新春キャンペーン

つまり、次のような結合従属がある。

{購入店舗} ---> {商品名} 
    │              ↕
    └ -----> {キャンペーン}

そこで、この結合従属を排除するため、キャンペーンマスタを次のように分割する。

(1) 購入店舗_商品名

購入店舗 (pk)商品名 (pk)
店舗ATシャツ
店舗Aジーンズ
店舗BTシャツ
店舗Bジーンズ
店舗Bワンピース

(2) 購入店舗_キャンペーン

購入店舗 (pk)キャンペーン (pk)
店舗A学生向けキャンペーン
店舗A在庫一掃キャンペーン
店舗B新春キャンペーン
店舗B在庫一掃キャンペーン

(3) 商品名_キャンペーン

商品名 (pk)キャンペーン (pk)
Tシャツ学生向けキャンペーン
Tシャツ新春キャンペーン
Tシャツ在庫一掃キャンペーン
ジーンズ学生向けキャンペーン
ジーンズ新春キャンペーン
ワンピース新春キャンペーン

参考資料



著者画像

ゆうき

2018/04からITエンジニアとして活動、2021/11から独立。主な使用言語はPython, TypeScript, SAS, etc.