0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

データベース設計の基本:正規化とテーブル設計の考え方【実例付き】

0
Posted at

はじめに

「とりあえず動くからこのテーブル構造でいいか」——そう作ったDBが、後から地獄を生むことがあります。データの重複、更新ミス、集計のしづらさ…。

この記事では、データベース設計の基本である「正規化」 を、具体的なダメな例とその改善を通して解説します。難しい用語より「なぜそうするのか」を重視しました。

なぜ設計が重要なのか

テーブル設計は家でいう基礎工事です。あとから変更するほどコストが跳ね上がります。最初に少し時間をかけるだけで、その後の開発・運用がずっと楽になります。


ダメな例:1つのテーブルに全部詰め込む

注文管理を例にします。最初はこう作りがちです。

orders テーブル(悪い例)

order_id customer_name customer_email product1 product2 product3
1 田中太郎 tanaka@x.com りんご みかん
2 田中太郎 tanaka@x.com バナナ

何が問題か

  1. 商品が3つまでしか登録できない(product4が必要になったら?)
  2. 顧客情報が重複している(田中さんがメール変更したら全行を直す必要がある)
  3. 「りんごが何件売れたか」を集計しにくい(3つの列を横断して数える羽目に)

第1正規化:繰り返しを別の行にする

「product1, product2, product3」のような繰り返し列をなくし、1行1商品にします。

order_items テーブル

order_id product_name
1 りんご
1 みかん
2 バナナ

これで商品は何個でも登録できるようになり、「りんごの売上件数」も WHERE product_name = 'りんご' で簡単に集計できます。


第2正規化:重複する情報を別テーブルに分ける

顧客情報が注文ごとに重複しているのが問題でした。顧客は独立した存在なので、別テーブルに切り出します。

customers テーブル

customer_id name email
1 田中太郎 tanaka@x.com

orders テーブル

order_id customer_id order_date
1 1 2025-04-01
2 1 2025-04-03

顧客情報は customers に1件だけ。メールアドレスを変更しても1箇所直すだけで済みます。orderscustomer_id で顧客を参照します。


完成形:3つのテーブルに分割

最終的にこうなります。

customers(顧客)
  ├ customer_id(主キー)
  ├ name
  └ email

orders(注文)
  ├ order_id(主キー)
  ├ customer_id(外部キー → customers)
  └ order_date

order_items(注文明細)
  ├ item_id(主キー)
  ├ order_id(外部キー → orders)
  ├ product_name
  ├ quantity
  └ price

テーブル作成SQL

CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    name        VARCHAR(100) NOT NULL,
    email       VARCHAR(255) NOT NULL UNIQUE
);

CREATE TABLE orders (
    order_id    INT AUTO_INCREMENT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date  DATE NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
);

CREATE TABLE order_items (
    item_id      INT AUTO_INCREMENT PRIMARY KEY,
    order_id     INT NOT NULL,
    product_name VARCHAR(100) NOT NULL,
    quantity     INT NOT NULL,
    price        INT NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(order_id)
);

分割したデータを結合して取得する

テーブルを分けても、JOINで元の形に戻せます。

SELECT
    o.order_id,
    c.name,
    o.order_date,
    oi.product_name,
    oi.quantity,
    oi.price
FROM orders o
JOIN customers c   ON o.customer_id = c.customer_id
JOIN order_items oi ON o.order_id    = oi.order_id
ORDER BY o.order_id;

設計のときに意識する3つのポイント

① 1つのテーブルには1種類のものだけ

顧客・注文・商品は別の概念です。「customers」「orders」のように、1テーブル=1つの概念にします。

② データの重複をなくす

同じ情報が複数の場所にあると、更新時にズレます。「1つの事実は1箇所に」が原則です。

③ 主キーと外部キーで関連を表現する

各テーブルに一意の主キー(customer_id など)を持たせ、別テーブルからは外部キーで参照します。これで「どの注文がどの顧客のものか」を正しく表現できます。


過剰な正規化に注意

一方で、なんでも分割すればいいわけではありません。

  • JOINが増えすぎてクエリが複雑になる
  • 参照のたびに複数テーブルをまたぐので速度が落ちる場合がある

集計が中心のシステム(分析・レポート用途)では、あえて非正規化して1テーブルにまとめることもあります。正規化は目的ではなく手段です。用途に応じて使い分けます。


まとめ

  • テーブル設計は後から直すほど高コスト。最初が肝心
  • 第1正規化:繰り返し列をなくして1行1データに
  • 第2正規化:重複する情報を別テーブルに切り出す
  • 主キー・外部キーで関連を表現する
  • ただし過剰な正規化は逆効果。用途で使い分ける

「1つの事実は1箇所に」を意識するだけで、設計の質は大きく変わります。


データベース設計・最適化のご相談

「テーブル設計を最初から相談したい」「既存DBの構造を見直したい」「データ移行をお願いしたい」などのご相談を受け付けています。

🌐 https://datarou.com

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?