はじめに
データベースを設計するときに必ず話題に出るのは、「正規化」と「非正規化」についてです。
正規化は冗長なデータを排除して一貫性を保つ基本的な手法ですが、実務ではあえて非正規化して冗長にデータを持たせる設計もよく行われます。
この記事では、正規化と非正規化の違いや、それぞれのメリット・デメリットを整理し、どういう状況でどちらを選択すべきかを解説していきます。
正規化とは
正規化とは、データの冗長性を排除し、整合性を保ちやすくするための設計手法。
正規化の段階
第1正規形(1NF)
1つのセルには1つのデータだけを入れるようにする。
(例)電話番号を「tel1, tel2, tel3」と列に持たず、別テーブルに分ける。
(同じ意味の値を「カラムの繰り返し」で表しているから「1セル1値」のルールに反している)
正規化前
customers
---------------------------------------------
id | name | tel1 | tel2 | tel3
---------------------------------------------
1 | 田中 | 090-xxxx | 03-xxxx | NULL
正規化後
customers
------------
id | name
------------
1 | 田中
customer_phones
---------------------------------------------
id | customer_id | phone_type | phone_number
---------------------------------------------
1 | 1 | mobile | 090-xxxx
2 | 1 | home | 03-xxxx
【正規化前のデメリット】
①上限が決まってしまう
「電話番号は3つまで」って勝手に制限がかかる。将来「4つ目」が必要になったらカラム追加という設計変更が必要。
②検索や集計がややこしい
例えば「03で始まる番号を持つ顧客」を探す時、tel1, tel2, tel3 全部に検索条件を書かなきゃいけない。
③NULLだらけになる
ほとんどの顧客は電話番号1つだけなのに、tel2, tel3 が常に空欄になる。
第2正規形(2NF)
主キーの一部にしか関係しないデータは別テーブルに移す。
(例)受注テーブル(注文ID+商品IDがキー)に「顧客住所」を持っていたら分離する。
顧客情報を別テーブルに持っている
orders
--------------------------------
order_id | product_id | address
--------------------------------
1 | A001 | 東京都港区...
2 | B003 | 東京都港区...
正規化後
customers
----------------------
id | name | address
----------------------
1 | 田中 | 東京都港区...
orders
-----------------------------------
order_id | product_id | customer_id
-----------------------------------
1 | A001 | 1
2 | B003 | 1
第3正規形(3NF)
主キーと直接関係のないデータは別テーブルに移す。
(例)社員テーブルに「部署ID」と「部署名」が両方入っていたら、部署名は部署マスタに分ける。
employees
------------------------------------------------------
employee_id | name | department_id | department_name
------------------------------------------------------
1 | 佐藤 | D01 | 営業部
2 | 鈴木 | D01 | 営業部
3 | 高橋 | D02 | 開発部
正規化後
departments
----------------------
id | name
----------------------
D01 | 営業部
D02 | 開発部
employees
-------------------------------------
employee_id | name | department_id
-------------------------------------
1 | 佐藤 | D01
2 | 鈴木 | D01
3 | 高橋 | D02
非正規化とは
非正規化(Denormalization)は、あえて冗長性を持たせる設計のこと。
メリット
過去の状態を保持できる
非正規化は「当時の情報を残す」ためにも有効。
たとえば請求書に「発行当時の住所」や「当時の顧客名」を保存しておけば、後から顧客が引っ越したり改名しても、過去の請求書を正しく再現できる。
単なる冗長ではなく、履歴として意味を持たせる設計。
読み取りが速くなる
JOINを使わずに1つのテーブルだけで情報が完結するため、クエリがシンプルになる。
たとえば「請求書一覧」を出すときに顧客テーブルとJOINしなくても済むので、読み取り速度が向上するケースがある。
集計処理が楽になる
必要な情報が1テーブルにまとまっていると、SUMやCOUNTなどの集計処理をシンプルに書ける。
特に帳票系のシステムでは、非正規化された方がSQLがスッキリし、実装工数も減らせる。
可視化やアウトプット用途に適している
請求書テーブルに「顧客名」を持たせておけば、請求書を生成するときに顧客テーブルと結合する必要がない。
ワンショットで帳票が作れるので、パフォーマンス的にも運用的にも楽になる。
正規化と非正規化の使い分け
正規化と非正規化は「どちらが正しいか」ではなく、「どのような場面だからどっちがいい」で決める。
正規化すべき場面
更新が多く、一貫性を保つことが重要なデータは正規化しておくべき。
- 更新頻度が高いデータ
情報が頻繁に変わるなら、冗長に持たない方がミスや不整合を防げる。 - 整合性が重要なマスタ系
代表的なのは顧客、商品、契約条件など。
もし冗長に持ってしまうと、1か所を更新し忘れただけでデータが食い違ってしまう。
非正規化すべき場面
一方で、参照や集計が中心で「読み取り性能」や「過去の状態保持」が求められるデータは、非正規化した方がメリットが大きい。
- 読み取り・集計が圧倒的に多いデータ
請求書や売上レポート、ダッシュボード用スナップショットなど。
JOINを減らし、SQLをシンプルにできる。
まとめ
正規化が「正しい」とされると思っていましたが、実務では非正規化も重要なことがあることを学びました。
特にSaaSや帳票システムでは、「過去を正しく再現するための冗長性」が重要になると実感しました。