正規化 (1NF〜3NF)

正規化は「理論暗記」ではなく、更新不整合を防ぐための実務技法です。 このページでは、受注データを例に非正規 → 1NF → 2NF → 3NF の順で段階的に分解します。

このページは概念理解を優先して、フィールド名を日本語で記載しています。 実装時に英語名へ置き換えても、正規化の考え方は同じです。

正規化の目的

非正規の問題

まずは、全部入りの 1 テーブルを考えます。

受注まとめ(
  受注ID, 受注日,
  顧客ID, 顧客名,
  商品ID, 商品名, 単価,
  数量
)
受注ID顧客名商品名単価数量
1001山田太郎USBメモリ 32GB12002
1001山田太郎キーボード38001
1002鈴木花子USBメモリ 32GB12001

この構造だと、商品名変更時に複数行更新が必要です。1行でも更新漏れがあると同じ商品IDなのに商品名が食い違います。

月別12カラム問題(代表的アンチパターン)

正規化の観点で特に多いのが、1レコードに月別値を横持ちする設計です。

商品別売上(
  商品ID,
  1月売上, 2月売上, 3月売上, ... , 12月売上
)

この設計は次の問題を起こします。

改善例(縦持ち):

月次売上(
  商品ID,
  対象年月,
  売上金額,
  PRIMARY KEY (商品ID, 対象年月)
)

「増える可能性がある軸(年月、週、タグ)」は列ではなく行で持つのが基本です。 正規化の実務的なコツとして覚えると、設計レビューで強くなります。

第1正規形 (1NF)

STEP 1: 1セル1値にする

1NF の要点は「繰り返し項目を列内に詰め込まない」ことです。 例えば 商品ID一覧='P01,P02' のような格納を避け、1明細1行に分けます。

悪い例良い例
商品ID一覧='P01,P02' 受注明細テーブルを作って 1 商品 1 行で保存

第2正規形 (2NF)

STEP 2: 部分関数従属を分離

明細テーブルの主キーを (受注ID, 明細番号) とすると、 顧客名受注ID だけで決まります。 これは主キーの一部にしか依存しないため、受注明細に置くべきではありません。

2NF の分離例:

受注(受注ID, 受注日, 顧客ID)
受注明細(受注ID, 明細番号, 商品ID, 数量)

第3正規形 (3NF)

STEP 3: 推移的関数従属を分離

例えば 受注顧客名 を持つと、 受注ID -> 顧客ID -> 顧客名 の推移依存が発生します。 顧客名は顧客テーブルへ分離するのが 3NF の考え方です。

3NF の到達形:

顧客(顧客ID, 顧客名)
商品(商品ID, 商品名, 単価)
受注(受注ID, 受注日, 顧客ID)
受注明細(受注ID, 明細番号, 商品ID, 数量)

ER図で見る差

非正規(責務が混在)

受注まとめ 受注ID (PK), 受注日 顧客ID, 顧客名 商品ID, 商品名, 単価 数量 受注・顧客・商品の責務が1表に混在している
1つのテーブルに複数概念が混在し、更新漏れが起きやすい状態。

3NF(責務分離後)

顧客 顧客ID (PK) 顧客名 受注 受注ID (PK) 受注日 顧客ID (FK) 受注明細 受注ID (PK/FK) 明細番号 (PK) 商品ID (FK), 数量 商品 商品ID (PK) 商品名, 単価 1:N 1:N N:1
責務単位でテーブル分離し、外部キーで関係を明示。更新不整合が起きにくい。

実務のまとめ

レビューで「この列はどのキーに従属するか」を説明できれば、正規化の品質が一段上がります。