第3正規形 (3NF)
第3正規形とは、第2正規形を満たし、かつ非キー属性が候補キー以外の非キー属性を経由して関数従属していない (推移関数従属を持たない) リレーションの状態をいう。
第3正規形のルール: 「別の列を経由して決まる列」を追い出す
第3正規形 (3NF) のルールは 2 つ。
- まず 第2正規形 を満たしていること
- 主キー以外の列が、別の「主キーでない列」を経由して決まっている 状態があってはいけない。
「経由して決まる」を専門用語で 推移関数従属 (transitive functional dependency) と呼ぶ。注文ID → 顧客ID → 顧客名 のように、真ん中の 顧客ID (主キーではない列) を挟んで 注文ID から顧客名が「間接的に」決まっているような状態。 こうなっていると同じ顧客の情報が注文の数だけ何度も繰り返され、更新時異常 の原因になる。
実践例: 2NF まで進めた受注テーブル
前ページ で 2NF に進めた結果、 受注データは 受注 / 商品 / 受注明細 の 3 テーブルに分かれた。 このうち 商品 と 受注明細 はもう分析するべき従属関係は残っていない。 問題は 受注 テーブル — ここに顧客関連の列 (顧客名・顧客住所) がまだ残っており、 これが 3NF で扱う対象になる。
実データで見る冗長: 顧客ID が同じ行で顧客名・顧客住所が繰り返される
まず 2NF 受注テーブルを、列がどう決まっているかで色分けして眺める。 すると「同じ顧客ID の行で顧客名・顧客住所が全く同じ値を繰り返している」ことが目で見える。 これは 2NF まで進めても残っている冗長で、次に「なぜこうなるか」を関数従属で分析する。
◆ 注文IDPK | ■ 注文日 | ■ 顧客ID | ● 顧客名 | ● 顧客住所 |
|---|---|---|---|---|
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 |
| O002 | 2026-06-02 | C02 | 山田工業 | 大阪 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 |
● の列 (顧客名・顧客住所) は、■ の 顧客ID が同じなら必ず同じ値になる (O001 と O003 は共に C01 なので田中商事/東京)。これは顧客の情報が「注文行ごとに複製されている」ことを意味する。
関数従属で分析すると推移経路が見つかる
上で見えた冗長がなぜ起きているかを関数従属で分析すると、注文ID → 顧客ID → 顧客名/顧客住所 という非キー属性を経由した 従属経路が見えてくる。
顧客ID は候補キーではない (受注テーブルの候補キーは 注文ID)。それなのに顧客ID から顧客名・顧客住所が決まっている。つまり注文ID → 顧客名・顧客住所は「注文ID → 顧客ID → 顧客名/顧客住所」という経由でしか成立しておらず、これが 3NF が排除したい推移関数従属。
3NF 化: 経由先を別テーブルに切り出す
経由の元 (顧客ID) と経由先 (顧客名・顧客住所) をまとめて 顧客 テーブルとして独立させる。 受注テーブル側には 顧客ID だけを残しておけば、 顧客の詳細が知りたい時は顧客テーブルを見に行けば分かる。
| 注文IDPK | 注文日 | 顧客ID | 顧客名 | 顧客住所 |
|---|---|---|---|---|
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 |
| O002 | 2026-06-02 | C02 | 山田工業 | 大阪 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 |
| 注文IDPK | 注文日 | 顧客ID |
|---|---|---|
| O001 | 2026-06-01 | C01 |
| O002 | 2026-06-02 | C02 |
| O003 | 2026-06-03 | C01 |
| 顧客IDPK | 顧客名 | 顧客住所 |
|---|---|---|
| C01 | 田中商事 | 東京 |
| C02 | 山田工業 | 大阪 |
顧客ID だけが残る。 これで顧客の名前や住所を変更する時も 1 行だけ更新すれば、 その顧客が持つ全ての注文の表示に自動で反映される。3NF 化 完了後のスキーマ全体像
1NF から出発して、2NF・3NF と分割してきた結果、 最終的に以下の 4 テーブル のスキーマになる。
受注 (注文ID, 注文日, 顧客ID)— 注文そのものの事実顧客 (顧客ID, 顧客名, 顧客住所)— 顧客マスタ商品 (商品ID, 商品名, 単価)— 商品マスタ受注明細 (注文ID, 商品ID, 数量)— 注文と商品の関連
非1NF スプレッドシートに詰め込まれていたデータが、3 段階の正規化を経て、 「顧客の事実は顧客テーブルに 1 度」「商品の事実は商品テーブルに 1 度」「注文の事実は受注テーブルに 1 度」 という 一事実一箇所 の構造になった。
練習問題
別のデータで自分でも試してみる。答えを見る前にどこに推移があるか考えてみてほしい。
社員ID の単一主キー。 推移関数従属を見つけ、3NF に分割せよ。| 社員IDPK | 氏名 | 部署ID | 部署名 | 部署所在地 |
|---|---|---|---|---|
| E001 | 山田 | D01 | 営業部 | 東京 |
| E002 | 田中 | D01 | 営業部 | 東京 |
| E003 | 佐藤 | D02 | 開発部 | 大阪 |
| E004 | 鈴木 | D03 | 人事部 | 東京 |
答えを見る
関数従属の分析:
社員ID → 氏名, 部署ID(直接)部署ID → 部署名, 部署所在地(部署ID は非キー属性)社員ID → 部署名, 部署所在地(推移 = 3NF 違反)
部署関連 (部署名・部署所在地) を 部署 テーブルに切り出し、 社員テーブルには 部署ID だけを外部キーとして残す。
| 社員IDPK | 氏名 | 部署ID |
|---|---|---|
| E001 | 山田 | D01 |
| E002 | 田中 | D01 |
| E003 | 佐藤 | D02 |
| E004 | 鈴木 | D03 |
| 部署IDPK | 部署名 | 部署所在地 |
|---|---|---|
| D01 | 営業部 | 東京 |
| D02 | 開発部 | 大阪 |
| D03 | 人事部 | 東京 |
3NF の先には BCNF もあるが…
3NF より少し厳密な ボイス・コッド正規形 (BCNF) というものもある。 3NF が排除しきれない特殊なケース (候補キーが複数あって、その候補キー同士に従属関係があるとき) をカバーする。
ただし現実の業務モデルではこの特殊ケースは滅多に問題にならないため、実務では 3NF まで進めば十分 とされることが多い。 BCNF の詳しい話は別トピックで扱う予定 (現状は未収録)。
3NF まで進めるとこんな良いことがある
- 「同じ情報を複数の場所に書く」問題がほぼ解消され、更新時異常の心配がなくなる
- 顧客・商品・部署のような「マスタ (基準情報)」が自然に独立したテーブルになる
- その後の外部キー制約・インデックス設計・トランザクション設計といった議論が単純になる
ここまで進めた上で、性能上どうしても必要な箇所についてだけ、 意識的に冗長を戻す 非正規化を検討するのが実務の定番の流れ。
よくある疑問
関連トピック
- 基礎なぜ正規化が必要か
正規化されていないテーブルでは、同じ事実を複数の行に重複して持つために、挿入・更新・削除の各操作で矛盾や情報損失が発生する。この「更新時異常」を体系的に排除するのが正規化の目的である。
- 基礎関数従属性
属性 X の値が決まると属性 Y の値も一意に決まる関係を関数従属という。この概念は 1NF〜3NF、BCNF まで正規化の全ステップの判定基準になる。部分関数従属・推移関数従属など派生概念も整理する。
- 基礎キーの階層
正規化の議論に入る前に、スーパーキー・候補キー・主キーの3階層と、外部キー・代替キー・複合キーとの関係を整理する。2NF/3NF の「非キー属性」「部分従属」を語るための語彙を揃える。
もっと学びたい方へ(おすすめ書籍)
テーブル設計と正規化、パフォーマンス考慮のインデックス設計まで実務レベルで学べる定番書。第2版ではクラウド対応も強化。
IPAデータベーススペシャリスト試験の総合対策書。インデックス関連は本サイトと合わせて学ぶと理解が深まる。
リレーショナルモデルの理論から、インデックス設計を含む実務で使えるSQLまで解説。
SQLの本質的な使い方と、インデックスが効くクエリの書き方を学べる。ウィンドウ関数など現代SQLも網羅。
実務でやりがちなSQL・DB設計のアンチパターンとその回避策を体系的に学べる。
PostgreSQLの内部構造・ストレージ・インデックス機構を丁寧に解説。設計と運用計画の鉄則が学べる。
本セクションはAmazonアソシエイトのリンクを含みます。
もっと深くDBを学びたい方へ。
たいてっくが、SQL・データベース設計・パフォーマンスチューニング・ IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。