本文へスキップ
BecomeCoder

SQL テーブル設計 (DDL)コース · 第1章 テーブルを設計する · レッスン6

正規化 ― 重複を断つ設計の型

ブラウザで完結

導入

第0章で「重複を避けるために表を分ける」ことを 正規化 と呼ぶ、と学びました。この章の中心テーマとして、正規化を**段階(第1〜第3正規形)**として、もう一歩踏み込んで理解します。難しく感じるかもしれませんが、根っこは「1つの事実は1か所にだけ書く」というシンプルな原則です。

説明

もし何も設計せず、注文を1枚の“ベタ表”で持ったらどうなるか。悪い例から始めます。

正規化前(ダメな1枚表)

注文会員都市商品(複数)単価
5田中福岡ノートPC, USBケーブル128000, 800

この表には問題が詰まっています。「商品」欄に複数値が押し込まれ、会員名や都市は注文のたびに繰り返され、単価は商品の情報なのに注文表に混ざっています。これを3段階で整えます。

flowchart LR
    R0["非正規形<br/>1マスに複数値・重複だらけ"] -->|"第1正規化<br/>1マス1値にする"| R1["第1正規形(1NF)<br/>繰り返しを行に分解"]
    R1 -->|"第2正規化<br/>主キーの一部で決まる列を分離"| R2["第2正規形(2NF)"]
    R2 -->|"第3正規化<br/>キー以外に依存する列を分離"| R3["第3正規形(3NF)<br/>重複のない設計"]
  • 第1正規形(1NF)… 1マスに1つの値。「商品」欄に複数の商品を詰めるのをやめ、1商品=1行にばらします。これで order_items(注文明細)が生まれます。
  • 第2正規形(2NF)… 主キーの“一部”で決まる項目を追い出す。明細のキーは「注文+商品」の組ですが、「商品の単価」は商品だけで決まります。だから単価は products 側へ。商品マスタの独立です。
  • 第3正規形(3NF)… キー以外の項目に“ぶら下がる”項目を追い出す。注文表に会員名や都市を持つと、それは「会員」で決まる情報。だから users 側へ。会員マスタの独立です。

こうして整えた結果が、まさにこのショップ DB の5テーブル構成です。「注文」「明細」「会員」「商品」「カテゴリ」に分かれ、どの事実も1か所にしか書かれていない。だから、会員が引っ越しても、商品の単価が変わっても、直すのは1行だけで済みます。

-- 正規化された5表から、あの“1枚表”をいつでも再現できる(JOIN で組み立て直す)
SELECT o.id AS 注文, u.name AS 会員, p.name AS 商品, oi.quantity AS 個数
FROM order_items oi
JOIN orders   o ON oi.order_id   = o.id
JOIN users    u ON o.user_id     = u.id
JOIN products p ON oi.product_id = p.id
ORDER BY o.id;

正規化は「表を分けて保存し、見たいときは JOIN で組み立てる」という分業です。保存は正規化、表示は結合――この呼吸が実務設計の基本になります。

ほどよさも大切です。集計を速くしたい等の理由で、あえて重複を許す「非正規化」を部分的に行うこともあります。まずは3NF を基本形として身につけ、崩すのはその後です。

やってみよう

初期表示のクエリを実行し、正規化された5つの表から「注文・会員・商品・個数」の“1枚表”が再現できることを確認しましょう。バラバラに保存しても、JOIN でいつでも元の見え方に戻せる――これが正規化の安心感です。

演習

同じ5表の JOIN を使って、注文ごと(o.id)の合計金額SUM(oi.quantity * p.price))を取り出してください。

ヒント1を見る

GROUP BY o.id でまとめ、金額は SUM(oi.quantity * p.price)

ヒント2を見る

SELECT o.id, SUM(oi.quantity * p.price) AS 合計 FROM order_items oi JOIN products p ON oi.product_id=p.id GROUP BY o.id;

実際に動かしてみよう

下のエディタにクエリを書いて「実行」を押すと、ブラウザ内のSQLiteで結果が表示されます。本文の例をそのまま試したり、書き換えたりしてみましょう。

SQL — ブラウザ内で実行(SQLite)

ブラウザ内でSQLを動かす環境(SQLite)を読み込みます(初回のみ一瞬)。
スクロールして表示された時点でも自動で読み込まれます。