はじめに
LLM に SQL を書かせる、エージェントに DB を触らせる、CSV を投げて分析させる。AI にデータ仕事を任せる場面が一気に増えました。
このとき効いてくるのが 元データがどれくらい整っているか です。同じ問いでも、データが綺麗に保存されているかどうかでAI が返す答えの精度も、そもそも答えを返せるかどうかも大きく変わってくるので、AI時代においてデータを正しく取り扱えるかという能力は一段と重要になってきています。
この記事ではデータを綺麗に保管する手法の基礎である 正規化 について紹介します。
正規化は 達人に学ぶ DB 設計徹底指南書 第 2 版 でも紹介されており、正直この本を読めば正規化については完璧なんですが、この本を読む前段階としてざっと正規化について一通り学ぶという目的でこの記事を活用頂ければと思います。
第一 → 第二 → 第三正規化 まで通してみます。各段階でテーブルがどう変わるかを目で追う形で進めるので、読み終わる頃には「なぜこう分けるか」の勘所が掴めるはずです。
より詳しい図解や練習問題は、解説サイト側にまとめています。
出発点: Excel でよくある受注シート
まずは題材を用意します。「Excel で管理されがちな受注シート」で、1 行が 1 注文、その注文の中身(商品情報)を「商品明細」というセルにまとめて詰め込んでいる形です。この記事ではこの 1 つのデータを最後まで持ち回して、各正規形での変化を追いかけます。
受注(非1NF)
| 注文ID | 注文日 | 顧客ID | 顧客名 | 顧客住所 | 商品明細 |
|---|---|---|---|---|---|
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 | P01:ノート:300×2, P02:ペン:150×1 |
| O002 | 2026-06-02 | C02 | 山田工業 | 大阪 | P01:ノート:300×5 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 | P02:ペン:150×3, P03:消しゴム:100×4 |
見た目はスッキリして扱いやすそうですが、この形のまま SQL の世界に持ち込むと「O001 で買われた商品を集計したい」「単価が変わったから全ての行を書き換えたい」といった素朴な操作ができません。
ここから 3 段階かけて、SQL や AI から扱いやすい形に育てていきます。
第1正規化: 1 セルに 1 つの値だけにする
第1正規形(1NF)のルールは 2 つだけ。
- 1 セルに 1 つの値だけ(カンマ区切りで複数値を詰めない)
- 同じ意味の列を横に並べない(「電話1・電話2・電話3」みたいなことをしない)
上の受注シートは「商品明細」セルに複数値を詰めているので、1NF 違反です。値の数だけ行をコピーして、1 セル 1 値に開いてあげます。
受注(1NF)
| 注文ID | 注文日 | 顧客ID | 顧客名 | 顧客住所 | 商品ID | 商品名 | 単価 | 数量 |
|---|---|---|---|---|---|---|---|---|
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 | P01 | ノート | 300 | 2 |
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 | P02 | ペン | 150 | 1 |
| O002 | 2026-06-02 | C02 | 山田工業 | 大阪 | P01 | ノート | 300 | 5 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 | P02 | ペン | 150 | 3 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 | P03 | 消しゴム | 100 | 4 |
やったことは「商品明細セルを分解して行を増やしただけ」で、テーブルは 1 つのままです。別テーブルに切り出すのは 1NF ではやりません。次の 2NF から始まる話。
この時点で 注文ID だけを条件にした集計や、商品ID で絞った集計が SQL で自然に書けるようになりました。
一方で、同じ注文 (O001) の 2 行では 注文日・顧客名・顧客住所 が完全にコピーされているし、同じ商品 (P01) の 2 行では 商品名・単価 も重複しています。この重複を消していくのが次の 2NF・3NF の仕事です。
第2正規化: 主キーの「一部だけで決まる列」を追い出す
1NF まで進めた受注テーブルは、注文ID 単独でも 商品ID 単独でも 1 行に絞れません。両方を組み合わせた (注文ID, 商品ID) の複合キーが主キーになります。
第2正規形(2NF)のルールは「複合キーの 一部だけで値が決まってしまう列 があってはいけない」というもの。専門用語では 部分関数従属 と呼びますが、実データで見るとイメージがつきます。
上のテーブルを列ごとに眺めると、次の 3 グループに分けられます。
-
(注文ID, 商品ID)の組で初めて決まる: 数量 -
注文IDだけ で決まる: 注文日・顧客ID・顧客名・顧客住所 -
商品IDだけ で決まる: 商品名・単価
「注文ID だけで決まる列」が主キーの片方だけに依存している ── これが 2NF が排除したいパターンです。この 3 グループはそのまま「2NF 後に分かれる 3 テーブル」の原型になります。
受注
| 注文ID | 注文日 | 顧客ID | 顧客名 | 顧客住所 |
|---|---|---|---|---|
| O001 | 2026-06-01 | C01 | 田中商事 | 東京 |
| O002 | 2026-06-02 | C02 | 山田工業 | 大阪 |
| O003 | 2026-06-03 | C01 | 田中商事 | 東京 |
商品
| 商品ID | 商品名 | 単価 |
|---|---|---|
| P01 | ノート | 300 |
| P02 | ペン | 150 |
| P03 | 消しゴム | 100 |
受注明細
| 注文ID | 商品ID | 数量 |
|---|---|---|
| O001 | P01 | 2 |
| O001 | P02 | 1 |
| O002 | P01 | 5 |
| O003 | P02 | 3 |
| O003 | P03 | 4 |
商品名・単価は「商品」テーブルに 1 行ずつ集約されて重複が消えました。同じ商品名を書き直す作業はもう発生しません。
なお、主キーが 1 列だけのテーブルは「主キーの一部」というものが存在しないので、2NF 違反が起きようがありません。2NF を意識する必要があるのは複合キーのテーブルだけ と覚えておくと楽です。
第3正規化: 「別の列を経由して決まる列」を追い出す
2NF まで進めても、受注テーブルにはまだ顧客関連の列が残っています。ここに 顧客ID が同じ (C01) 行では 顧客名・顧客住所 が全く同じ値を繰り返している、という重複が残ります。
この重複が起きる理由を関数従属で書き下すと、
注文ID → 顧客ID → 顧客名, 顧客住所
というふうに、顧客名・顧客住所 は「非キー属性の 顧客ID を経由して」注文ID から決まっている状態になっています。これを 推移関数従属 と呼び、第3正規形(3NF)はこの経由を排除するルールです。
やることは 2NF と同じ発想で、経由の元 (顧客ID) と経由先 (顧客名・顧客住所) をまとめて別テーブルに切り出します。
受注
| 注文ID | 注文日 | 顧客ID |
|---|---|---|
| O001 | 2026-06-01 | C01 |
| O002 | 2026-06-02 | C02 |
| O003 | 2026-06-03 | C01 |
顧客
| 顧客ID | 顧客名 | 顧客住所 |
|---|---|---|
| C01 | 田中商事 | 東京 |
| C02 | 山田工業 | 大阪 |
これで「田中商事の住所が変わったら 1 行だけ更新すれば、その顧客の全注文の表示に自動で反映される」状態になります。同じ情報を何箇所も直して回る必要がありません。
最終形: 4 テーブルのスキーマ
非1NF の Excel シートから出発して、3 段階の分解を経て、最終的にこの 4 テーブルに落ち着きました。
-
受注 (注文ID, 注文日, 顧客ID)— 注文そのもの -
顧客 (顧客ID, 顧客名, 顧客住所)— 顧客マスタ -
商品 (商品ID, 商品名, 単価)— 商品マスタ -
受注明細 (注文ID, 商品ID, 数量)— 注文と商品の関連
出発時の 1 枚のシートに詰め込まれていた情報が、「顧客の事実は顧客テーブルに 1 度」「商品の事実は商品テーブルに 1 度」「注文の事実は受注テーブルに 1 度」という 一事実一箇所 の構造になっています。
各段階の作業内容を並べると、
| 段階 | やること | ねらい |
|---|---|---|
| 1NF | セルに詰めた複数値をバラして行を増やす | 表として扱える最低限の形にする |
| 2NF | 複合キーの一部だけで決まる列を別テーブルに | マスタ的な情報の重複を消す |
| 3NF | 他の非キー列経由で決まる列を別テーブルに | 経由による重複を消す |
「重複を消す」という一貫した動機で、どこの重複を消すかを段階的に細かくしているだけと考えると見通しがよくなります。
もう一歩深めたい人へ
正規化は「なぜこの分割になるのか」を関数従属という道具で説明できるようになると、初めて手を動かせるようになります。この記事では実例を追う形で流しましたが、体系的に学ぶなら書籍が近道です。
冒頭でも紹介した通り、DB 設計の定番書として個人的に一番おすすめなのが、達人に学ぶ DB 設計徹底指南書 第 2 版 です。正規化の第1〜第5・BCNF までを実例つきで丁寧に扱っていて、そのあとの物理設計・インデックス設計まで一気通貫でつながります。第 2 版ではクラウド DB の話題も追加されているので、現代の実務にも合わせて読めます。
サイト側では正規化の各段階に対応する図解・練習問題を置いています。同じ受注データを別の切り口で追いたい方はこちらもどうぞ。
→ https://taitech.dev/data-modeling/normalization
バグ・ご意見は X @taitech_dev まで。
※ 本記事の書籍リンクは Amazon アソシエイト・プログラムを利用しています。