データベース正規化とは?1NF〜BCNF・4NF・5NFの違いと実務での最適な進め方
正規化とは — 概要
正規化(せいきか、Normalization)とは、関係データベース設計においてデータの冗長性(重複)を減らし、データ整合性を保ちやすくするためにスキーマを分割・整理する一連の手法です。E. F. Codd が提唱した理論を基礎に、いくつかの「正規形(Normal Form)」が定義されており、それぞれが満たすべき制約を提供します。目的は、挿入/更新/削除時の異常(アノマリー)を回避し、保守性と論理的な一貫性を高めることです。
正規化が求められる理由(利点と典型的な問題)
- 更新(Update)異常:同じ情報が複数行に分散していると、変更が一部だけに行われ不整合が生じる。
- 挿入(Insert)異常:ある情報を追加したいが、別の不要な情報が必須になって挿入できない場合がある。
- 削除(Delete)異常:ある行を削除した結果、本来残すべき別情報まで失われることがある。
- 冗長性の削減:同じデータを複数保持しないことでストレージ効率や一貫性が向上する。
- データ整合性の向上:関数従属性(Functional Dependency)に基づく整然とした構造は、制約による整合性維持を容易にする。
正規化の基本概念:関数従属性とキー
正規化を理解する鍵は「関数従属性(X → Y)」と「候補キー/主キー」です。関数従属性とは、属性集合 X の値が決まれば属性 Y の値が一意に定まる関係を指します。候補キーは行を一意に特定する最小の属性集合です。各正規形は、これらの依存関係がどのように存在してよいかを定義します。
代表的な正規形(概要と判定基準)
以下は一般に使われる正規形の要点です。各正規形は前の正規形を満たしたうえで追加条件を課します。
第一正規形(1NF)
- 属性が原子値(スカラー値)でなければならない。繰り返しグループや配列的列を持たないこと。
- テーブルは二次元の行列構造であり、各セルに単一の値が入る。
第二正規形(2NF)
- 1NFを満たし、かつ部分関数従属性が存在しないこと。
- 「部分従属性」とは、複合主キーの一部が非キー属性を決定する場合を指します。主キーが単一カラムなら2NFは自動的に満たされる。
第三正規形(3NF)
- 2NFを満たし、かつ推移的従属性(transitive dependency)がないこと。
- つまり、非キー属性は主キーに対してのみ直接従属し、他の非キー属性を介してキーに従属していてはならない。
ボイス・コッド正規形(BCNF)
- より厳密な3NF拡張で、すべての関数従属性 X → Y について X がスーパーキーでなければならない。
- 3NFを満たしていても、BCNF を満たさないケースが存在する(候補キーでない属性がキーを決定する場合など)。
第四正規形(4NF)
- 多値従属性(multivalued dependency)を排除する。非自明な多値従属性 X ↠ Y が存在する場合、X はスーパーキーでなければならない。
- 1人の社員が複数のスキルと複数の趣味を持つような場合、スキルと趣味を別テーブルに分ける必要があるというようなケース。
第五正規形(5NF / PJNF)
- 結合依存(join dependency)に関する正規形で、あるリレーションが複数の部分に分解されたときに元に戻す(結合する)際に情報損失がないことを要求する。
正規化の理論的要件:ロスレス分解と従属性保存
スキーマ分解の2つの重要な性質:
- ロスレス(lossless)分解:分解後に元のリレーションを結合しても行が増えたり欠けたりせず、元の関係を正確に再現できること。一般に、R を R1, R2 に分けたとき、R1 ∩ R2 が R1 か R2 のキーになっていればロスレスとなる(関数従属性の閉包を使って判定)。
- 従属性保存(dependency preservation):分解後に元の関数従属性集合をローカルにチェックできること。BCNF 分解はしばしばロスレスだが、従属性保存を失う場合があるため、実務では3NF のままにすることがある。
具体例:正規化のステップ(簡易ケース)
非正規化テーブル(例:受注データ)
OrdersRaw
(order_id, order_date, customer_id, customer_name, product_id, product_name, unit_price, quantity, ship_address)
この1テーブルだと、同じ顧客名や商品名が複数行で重複し、更新異常が起きやすい。
1NF:各セルが原子値であること(例えば商品を複数列で持つ構造をやめ、行を分ける)
2NF:複合キー(order_id, product_id)を想定したとき、customer_name は order_id に依存するが product_id には依存しない → 分割する。
3NF(分割後の理想形)
- Customers(customer_id, customer_name, ship_address, ・・・)
- Products(product_id, product_name, unit_price, ・・・)
- Orders(order_id, order_date, customer_id, ・・・)
- OrderItems(order_id, product_id, quantity, unit_price)
こうすることで顧客名や商品情報が一箇所にまとまり、更新はそのテーブルだけを更新すれば良くなる。
実務上の注意点とトレードオフ
- 性能(パフォーマンス):正規化は書き込み整合性を高めるが、結合(JOIN)が多発すると読み取り性能に影響する。OLTP 系では高い正規化が適するが、OLAP(分析)用途ではデンormalize やスター/スノーフレークスキーマが好まれる。
- インデックスの活用:検索性能はインデックスで改善可能。正規化でテーブル数が増えても、適切なインデックスやビューを用いれば性能を維持できる。
- デノーマライゼーション(非正規化):読み取り性能確保のために意図的に冗長性を導入することがある。キャッシュ、マテリアライズドビュー、集計テーブルなどが典型。
- トランザクションと一貫性:正規化により一貫性を保ちやすくなるが、分割された複数テーブルの更新はトランザクション管理が必要。
- NoSQLなど非関係型データベース:スキーマ設計の考え方が異なる。ドキュメント指向では読み取り効率やアクセスパターンに応じて冗長化(埋め込み)を行うことが一般的で、関係データベースの正規化原則をそのまま適用する必要はない。
実践手順(現場での進め方の例)
- 要件(どのようなクエリが多いか、読み書き比率、性能要件)を明確化する。
- 概念モデル(ER図)を作成し、実際のエンティティと属性を整理する。
- 関数従属性を洗い出し、候補キーを特定する。
- まず3NF(または業務要件に応じてBCNF)を目標に論理設計を行う。
- 性能問題が出れば、プロファイリングに基づきインデックス追加・パーティショニング・デノーマライズを検討する。
- 分解後はロスレス性と従属性保存を確認する(ツールや手計算で閉包を使って検証)。
よくある誤解と注意
- 「正規化=常に良い」は誤り。読み取り最適化が重要なシステムでは意図的に非正規化することが合理的。
- 「1つの正規形を満たせば十分」という考えも危険。業務やデータ特性によって必要な正規形は異なる。
- BCNF を追求すると従属性保存を失い、実務での整合性チェックが複雑になることがあるためバランスが必要。
簡単なSQL例(正規化後のテーブル生成)
CREATE TABLE Customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100),
ship_address TEXT
);
CREATE TABLE Products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100),
unit_price DECIMAL(10,2)
);
CREATE TABLE Orders (
order_id INT PRIMARY KEY,
order_date DATE,
customer_id INT REFERENCES Customers(customer_id)
);
CREATE TABLE OrderItems (
order_id INT REFERENCES Orders(order_id),
product_id INT REFERENCES Products(product_id),
quantity INT,
unit_price DECIMAL(10,2),
PRIMARY KEY (order_id, product_id)
);
まとめ(いつ正規化を止めるか)
正規化は設計の手段であり目的ではありません。データ整合性と保守性を優先するなら高い正規形を目指し、読み取り性能が優先なら部分的な非正規化やキャッシュ・マテリアライズドビューを併用します。基本は「要件に基づいた設計」と「性能計測に基づく最適化」です。
参考文献
- Database normalization — Wikipedia
- IBM: Database normalization
- Microsoft: データベースの正規化 (Access)
- Oracle: Normalization
- E. F. Codd: A Relational Model of Data for Large Shared Data Banks (1970)
- Functional dependency — Wikipedia


