コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
第2正規形
第 2 正規形
このレッスンで分かること
- 部分関数従属とは何か
- 複合主キーを持つテーブルが直面する 2NF の問題
- 2NF に進む具体的なリファクタ手順
第2正規形 とは
部分関数従属を排除し複合キーの依存を整理する。本レッスンでは、第2正規形 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
2NF の定義
第 2 正規形 (2NF) とは、1NF を満たしつつ、主キーの全体に対して関数従属している 状態を指します。逆に言えば、主キーの 一部 だけで決まる列があれば 2NF 違反です。これを 部分関数従属 (Partial Functional Dependency) と呼びます。
2NF は 複合主キーを持つテーブル でのみ問題になります。単一カラムの主キーなら、すでに 2NF を満たしています。
違反例
注文明細テーブルを考えます。主キーは (order_id, product_id) の複合キーです。
| order_id | product_id | quantity | product_name | product_price |
|---|---|---|---|---|
| 1 | 100 | 2 | Apple | 500 |
| 1 | 200 | 3 | Banana | 300 |
| 2 | 100 | 1 | Apple | 500 |
ここで関数従属を書き出します。
(order_id, product_id) -> quantity— 主キー全体が決まれば数量が決まる(OK)product_id -> product_name— 商品 ID だけで商品名が決まってしまう(部分従属)product_id -> product_price— 商品 ID だけで価格が決まる(部分従属)
product_name と product_price は 主キーの一部 だけで決まっています。これが 2NF 違反です。
何が起きるか
- 商品名を変更したい場合、その商品を含む全注文行を更新する必要がある
- まだ注文されていない商品を登録できない(挿入異常)
- ある商品の全注文を削除すると、商品マスタが消える(削除異常)
これは「ユーザー情報を注文行に書いていた」例と同じ構造の問題です。
2NF に直す手順
部分従属している列を 別テーブル に切り出します。
SQL クエリ
-- 違反前
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
product_name VARCHAR(50),
product_price INT,
PRIMARY KEY (order_id, product_id)
);
-- 違反後(2NF)
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(50),
price INT
);
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (product_id) REFERENCES products(id)
);これで product_name の変更は products の 1 行更新で済み、まだ注文されていない商品の登録も可能になります。
よくある間違い
価格は商品マスタに置いていいのか
「注文時点の価格」と「現在の商品価格」は本来別物です。マスタの価格を直接参照すると、過去の注文額が変わってしまいます。order_items には unit_price を スナップショット として保持するのが定石です。
SQL クエリ
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
unit_price INT, -- 注文時点の価格を記録
PRIMARY KEY (order_id, product_id)
);これは 2NF 違反ではありません。unit_price は (order_id, product_id) の組に従属しているからです。「いつの値か」が違えば別の属性 という発想です。
単一主キーなら 2NF を気にしなくていい
id 一本の主キーなら部分従属は定義上ありえません。実務では代理キー id を使うことで、2NF を自動的にクリアできます。だからこそ複合主キーには注意が必要です。
「主キーの一部だけで決まる列がないか」を毎回チェックする習慣をつけよう。
関数従属図の書き方
設計レビューでは、矢印を使って従属関係を可視化します。
プレーンテキスト
(order_id, product_id) --> quantity
--> unit_price
product_id --------------> name
--> price
order_id ----------------> user_id
--> created_at主キーの一部から伸びる矢印があれば、そこが切り出し候補です。
トレードオフ
2NF に進むと JOIN が増えます。商品名を取得するには order_items と products の結合が必要です。読み取りが多いシステムでは、ビューやマテリアライズドビューで対処します。
やってみよう
order_itemsにcategoryカラムを足すと 2NF 違反か考える(答え:商品カテゴリはproduct_idで決まるので違反)- 自分のプロジェクトで複合主キーを持つテーブルを洗い出す
- 各カラムが「主キー全体」「主キー一部」のどちらに従属しているか書き分ける
よくある質問
Q. なぜ正規化するのですか?
A. データの重複を排除し、更新時の不整合(更新異常・挿入異常・削除異常)を防ぐためです。第 3 正規形まで適用すると保守性が大きく上がります。一方で JOIN が増えるため、検索性能と保守性のバランスで非正規化することもあります。
Q. 1NF と 2NF の違いは?
A. 1NF は「各セルが単一値」、2NF は「主キーの一部だけに依存するカラムをテーブル分割」する形です。複合キーの片方だけに紐づく属性は別テーブルに切り出すと 2NF になります。1NF を満たさないと 2NF 以降の議論はできません。
Q. 全部正規化すれば最適ですか?
A. 理想ですが、JOIN が増えてパフォーマンスが落ちる場面では非正規化(カラム複製、計算結果のキャッシュ)も有効です。OLTP は 3NF、レポーティング用 DWH はスタースキーマ(非正規化)と、用途で使い分けるのが現代的なアプローチです。
次のレッスン
次は 第3正規形 で、部分関数従属を排除し複合キーの依存を整理する を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 第2正規形 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 第2正規形 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン