コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
第3正規形
第 3 正規形
このレッスンで分かること
- 推移関数従属とは何か
- 3NF 違反が引き起こす冗長性とその影響
- 3NF と BCNF の違い
第3正規形 とは
推移関数従属を排除し非キー属性の独立性を確保する。本レッスンでは、第3正規形 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
3NF の定義
第 3 正規形 (3NF) とは、2NF を満たしつつ、非キー列が他の非キー列に従属していない 状態です。「主キー以外の列同士で従属関係を持たない」と言い換えてもよいです。
主キー → 非キー列 → 別の非キー列、という連鎖を 推移関数従属 (Transitive Functional Dependency) と呼びます。これを排除するのが 3NF です。
違反例
社員テーブルを考えます。
| emp_id | name | dept_id | dept_name | dept_location |
|---|---|---|---|---|
| 1 | Alice | 10 | Engineering | Tokyo |
| 2 | Bob | 10 | Engineering | Tokyo |
| 3 | Carol | 20 | Sales | Osaka |
関数従属は次のようになります。
emp_id -> name— 主キー → 非キー(OK)emp_id -> dept_id— 主キー → 非キー(OK)dept_id -> dept_name— 非キー → 非キー(推移従属)dept_id -> dept_location— 非キー → 非キー(推移従属)
dept_name は emp_id から見て dept_id を経由して決まる 列です。これが推移従属で、3NF 違反です。
何が起きるか
Engineering 部門の所在地が Tokyo から Yokohama に変わったとします。すべての Engineering 所属社員の行を更新する必要があります。更新漏れがあれば、Tokyo と Yokohama が混在し、データの真実がわからなくなります。
新しい部門 Marketing を作りたくても、まだ所属社員がいないと employees テーブルに NULL 行を作るしかなく、これは明らかに不自然です。
3NF に直す手順
推移従属している列を 別テーブル に切り出します。
SQL クエリ
-- 違反前
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
dept_id INT,
dept_name VARCHAR(50),
dept_location VARCHAR(50)
);
-- 違反後(3NF)
CREATE TABLE departments (
id INT PRIMARY KEY,
name VARCHAR(50),
location VARCHAR(50)
);
CREATE TABLE employees (
id INT PRIMARY KEY,
name VARCHAR(50),
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES departments(id)
);これで部門情報の更新は departments の 1 行で完結し、空の部門も独立して作れます。
BCNF との違い
BCNF (Boyce-Codd Normal Form) は 3NF をさらに厳密にしたものです。3NF は「非キー列同士の推移従属を禁止」だけですが、BCNF は「全ての関数従属の決定項は候補キーでなければならない」と要求します。
実務で BCNF まで気にすることは稀です。3NF を満たしていれば、ほとんどの異常は防げます。逆に BCNF にしようとすると、稀に 関数従属性を保存できない 分解になることがあります。3NF は無損失分解と依存性保存の両方を常に満たせますが、BCNF は依存性保存を犠牲にする場合があるため、現場では 3NF で止めるのが定石です。
DRY 原則との関係
3NF はソフトウェア工学の DRY (Don't Repeat Yourself) 原則と同じ精神を持ちます。「同じ事実を 2 箇所に書かない」「真実は 1 箇所だけ」を守ることで、変更コストと不整合リスクを下げます。
トレードオフ
3NF にすると JOIN が増えます。社員一覧と部門名を一緒に表示するクエリは次のようになります。
SQL クエリ
SELECT e.name, d.name AS dept, d.location
FROM employees e
JOIN departments d ON e.dept_id = d.id;部門数 100、社員数 100 万、検索が 1 日 1 万回でも、インデックスを貼っておけば実用上問題ありません。「JOIN を恐れて非正規化する」のは早すぎる最適化です。
3NF まではほぼ無条件にやるべき。それ以上は 計測してから 判断すること。
設計レビューのチェックリスト
新しいテーブルを追加するとき、次の質問に答えられるか確認しましょう。
- 全カラムは原子値か(1NF)
- 複合主キーがあれば、各カラムは主キー全体に従属しているか(2NF)
- 主キー以外のカラム同士で従属関係はないか(3NF)
- 外部キーは正しく設定されているか
- 更新異常・挿入異常・削除異常は起きないか
やってみよう
employeesにzip_codeとcityを追加した場合、3NF 違反か考える(答え:zip_code -> cityなので違反)- 自分のテーブルから「非キー列同士で従属している」列を 3 組探す
- それぞれを別テーブルに切り出した
CREATE TABLEを書く
よくある質問
Q. なぜ正規化するのですか?
A. データの重複を排除し、更新時の不整合(更新異常・挿入異常・削除異常)を防ぐためです。第 3 正規形まで適用すると保守性が大きく上がります。一方で JOIN が増えるため、検索性能と保守性のバランスで非正規化することもあります。
Q. 1NF と 2NF の違いは?
A. 1NF は「各セルが単一値」、2NF は「主キーの一部だけに依存するカラムをテーブル分割」する形です。複合キーの片方だけに紐づく属性は別テーブルに切り出すと 2NF になります。1NF を満たさないと 2NF 以降の議論はできません。
Q. 全部正規化すれば最適ですか?
A. 理想ですが、JOIN が増えてパフォーマンスが落ちる場面では非正規化(カラム複製、計算結果のキャッシュ)も有効です。OLTP は 3NF、レポーティング用 DWH はスタースキーマ(非正規化)と、用途で使い分けるのが現代的なアプローチです。
次のレッスン
次は 非正規化のトレードオフ で、推移関数従属を排除し非キー属性の独立性を確保する を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 第3正規形 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 第3正規形 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン