MySQL の正規化(第1〜第3正規形)

正規化はリレーショナルデータベース設計の基礎理論で、データの冗長性を排除し、更新時の不整合を防ぐための手法です。実務で求められるのは主に第1〜第3正規形までで、これらを理解しておけば大半のテーブル設計に対応できます。

非正規形(正規化前)

まず、正規化されていない状態を見てみましょう。以下は1つのテーブルにすべての情報を詰め込んだ例です。

-- 非正規形:1つのセルに複数の値が入っている
-- | 注文ID | 顧客名  | 商品名          | 単価         |
-- |--------|---------|-----------------|--------------|
-- | 1      | 田中    | ペン, ノート    | 100, 300     |
-- | 2      | 鈴木    | 消しゴム        | 80           |

商品名と単価に複数の値がカンマ区切りで入っており、検索や集計が非常に困難です。この状態を解消するのが正規化の第一歩になります。

第1正規形(1NF)

第1正規形の条件は「各セルに格納する値が原子的(これ以上分割できない単一の値)であること」です。

CREATE TABLE orders_1nf (
  order_id INT,
  customer_name VARCHAR(50),
  product_name VARCHAR(100),
  unit_price DECIMAL(10,2),
  PRIMARY KEY (order_id, product_name)
);

-- 1行1商品に分解される
-- | order_id | customer_name | product_name | unit_price |
-- |----------|---------------|--------------|------------|
-- | 1        | 田中          | ペン         | 100        |
-- | 1        | 田中          | ノート       | 300        |
-- | 2        | 鈴木          | 消しゴム     | 80         |

これで各セルが1つの値を持つようになりましたが、customer_name が注文ごとに繰り返されるという冗長性が残っています。

第2正規形(2NF)

第2正規形の条件は「第1正規形を満たし、かつ部分関数従属がないこと」です。部分関数従属とは、複合主キーの一部だけで決まるカラムが存在する状態を指します。

部分関数従属がある状態

customer_name は order_id だけで決まるのに、複合主キー(order_id, product_name)の一部にしか依存していない。これが部分関数従属。

第2正規形に分解

order_id だけで決まるカラムを別テーブルに分離する。注文テーブルと注文明細テーブルに分けることで解消される。

-- 注文テーブル
CREATE TABLE orders (
  order_id INT PRIMARY KEY,
  customer_name VARCHAR(50) NOT NULL
);

-- 注文明細テーブル
CREATE TABLE order_items (
  order_id INT,
  product_name VARCHAR(100),
  unit_price DECIMAL(10,2) NOT NULL,
  PRIMARY KEY (order_id, product_name),
  FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

第3正規形(3NF)

第3正規形の条件は「第2正規形を満たし、かつ推移的関数従属がないこと」です。推移的関数従属とは、主キー以外のカラムを経由して決まるカラムが存在する状態です。

-- 第2正規形だが第3正規形ではない例
-- | employee_id | department_id | department_name |
-- department_name は department_id で決まる(推移的関数従属)

-- 第3正規形に分解
CREATE TABLE departments (
  department_id INT PRIMARY KEY,
  department_name VARCHAR(100) NOT NULL
);

CREATE TABLE employees (
  employee_id INT PRIMARY KEY,
  department_id INT NOT NULL,
  FOREIGN KEY (department_id) REFERENCES departments(department_id)
);

department_name が employees テーブルに残っていると、部署名を変更するときに全社員レコードを更新する必要があり、更新漏れによる不整合が起きるリスクがあります。

正規化の流れ

非正規形:セルに複数値がある

第1正規形:各セルを原子的にする

第2正規形:部分関数従属を排除する

第3正規形:推移的関数従属を排除する

実務での注意点

理論的にはボイス・コッド正規形(BCNF)や第4・第5正規形もありますが、実務で意識するのは第3正規形までで十分です。過度な正規化はテーブル数の増加と JOIN の多発を招き、かえってパフォーマンスを悪化させます。正規化はあくまで設計の出発点であり、パフォーマンス要件に応じて意図的に非正規化する判断も必要になります。