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?

データベース設計入門 第4回:テーブル同士の関係を設計する

0
Last updated at Posted at 2026-09-15

はじめに

前回の「データベース設計入門 第3回:テーブル・カラム設計の基本」では、

  • テーブル
  • カラム
  • レコード
  • 主キー
  • NULL
  • データ型
  • ID

など、テーブルを設計するうえでの基本について解説しました。

例えば、ECサイトであれば、

users
products
orders

のように、役割ごとにテーブルを分けてデータを管理します。

しかし、実際のシステムでは、それぞれのテーブルが完全に独立しているわけではありません。

例えば、

「この注文は、どのユーザーが行った注文なのか」

という情報が必要になります。

また、

「この注文には、どの商品が含まれているのか」

という情報も必要です。

つまり、テーブルを作ったあとは、

テーブル同士がどのような関係になっているのか

を考える必要があります。

このようなテーブル同士の関係を設計することを、一般にリレーション設計と呼びます。

今回は、テーブル同士の基本的な関係である、

  • 1対1
  • 1対多
  • 多対多

について、ECサイトを例にしながら説明します。

また、テーブル同士を関連付けるために重要となる、

  • 外部キー
  • 親テーブルと子テーブル
  • 中間テーブル

についても見ていきます。


この記事の対象者

この記事は、次のような人を対象にしています。

  • エンジニアとして開発をしているが、データベースについてあまり詳しくない人
  • SQLを書いた経験はあるものの、テーブル設計の経験が少ない人
  • テーブル同士をどのように関連付ければよいのか分からない人
  • 「1対多」「多対多」といった言葉を聞いたことはあるが、意味をよく理解していない人
  • データベース設計を体系的に学び直したい人

SQLの基本的な知識があると理解しやすいですが、リレーション設計の経験がなくても問題ありません。


テーブル同士の「関係」とは

まずは、ユーザー情報を管理するusersテーブルと、注文情報を管理するordersテーブルを考えてみます。

users
-------------------
id
name
email
created_at
orders
-------------------
id
user_id
ordered_at
total_amount

例えば、次のようなユーザーがいるとします。

id name email
1 山田太郎 taro@example.com
2 佐藤花子 hanako@example.com

そして、注文データが次のようになっているとします。

id user_id ordered_at
101 1 2026-09-01
102 1 2026-09-05
103 2 2026-09-07

ここで、ordersテーブルの

user_id

に注目してください。

注文ID「101」のuser_idは「1」になっています。

これは、

users.id = 1

のユーザー、つまり山田太郎さんが行った注文であることを表しています。

図にすると、次のようなイメージです。

users
id = 1  山田太郎
   ↑
   │
   │ user_id = 1
   │
orders
id = 101

このように、あるテーブルのデータを別のテーブルから参照することで、テーブル同士の関係を表現できます。


外部キーとは

前回の記事では、レコードを一意に識別するための**主キー(PRIMARY KEY)**について説明しました。

例えばusersテーブルでは、

users.id

を主キーとして利用できます。

そして、先ほどのordersテーブルでは、

orders.user_id

から、

users.id

を参照していました。

users
-------------------
id        ← 主キー
name
email
     ↑
     │
     │ 参照
     │
orders
-------------------
id
user_id   ← users.id を参照
ordered_at

このように、別のテーブルのキーを参照するために設定する制約を、

外部キー(FOREIGN KEY)

と呼びます。

外部キーを設定することで、

存在しないユーザーに対する注文

のような不整合をデータベース側で防ぐことができます。

例えば、usersテーブルに、

id = 1
id = 2

のユーザーしか存在しないとします。

その状態で、

orders.user_id = 999

という注文が登録されてしまうと、

「user_id = 999とは誰なのか?」

という状態になります。

適切に外部キー制約が設定されていれば、このような存在しないデータへの参照を防ぐことができます。

なお、

user_id

というカラムそのものを「外部キー」と呼ぶこともありますが、厳密には、そのカラムに対して設定される参照のルールが外部キー制約です。


テーブル同士の関係は大きく3種類ある

テーブル同士の基本的な関係には、次の3種類があります。

1対1
1対多
多対多

英語では、それぞれ、

1対1   → One-to-One
1対多  → One-to-Many
多対多 → Many-to-Many

と呼ばれます。

順番に見ていきましょう。


1対1

1対1とは、

ある1件のデータに対して、別のテーブルのデータが最大1件対応する関係

です。

例えば、ユーザーの基本情報とプロフィール情報を別々のテーブルで管理するとします。

ユーザー
   │
   │ 1
   │
   │ 1
プロフィール

テーブルとしては、例えば次のようになります。

users
-------------------
id
name
email
created_at
user_profiles
-------------------
id
user_id
birthday
phone_number
address

1人のユーザーに対して、プロフィールを1件だけ持てる設計です。

例えば、

id name
1 山田太郎
2 佐藤花子

というユーザーがいるとします。

プロフィールは、

id user_id birthday
1 1 1990-01-01
2 2 1995-05-10

となります。

users.id = 1

に対応するプロフィールは、

user_profiles.user_id = 1

の1件です。

「1件だけ」を保証する必要がある

ここで注意したいのが、

user_profiles.user_id

を作っただけでは、必ずしも1対1になるわけではないということです。

例えば、

user_id = 1
user_id = 1
user_id = 1

という3件のプロフィールを登録できてしまえば、1対多になってしまいます。

1ユーザーにつきプロフィールを1件だけにしたいのであれば、

user_profiles.user_id

に重複を許さない制約を設定するなどして、

同じユーザーに対するプロフィールは1件だけ

というルールをデータベース側でも保証する必要があります。


1対1なら必ずテーブルを分けるのか

ここで、

「1対1なら、最初から全部usersテーブルに入れてもよいのでは?」

と思うかもしれません。

その考え方も間違いではありません。

例えば、

users
-------------------
id
name
email
birthday
phone_number
address

としても設計できます。

そのため、

1対1の関係があるから必ずテーブルを分割する

というわけではありません。

例えば、

  • 普段は利用しない情報を分離したい
  • 特定機能でだけ使用する情報を分けたい
  • セキュリティや権限の都合で分離したい
  • テーブルごとの役割を明確にしたい

といった理由がある場合に、別テーブルにすることがあります。

重要なのは、

なぜテーブルを分けるのか

を説明できることです。


1対多

実際のデータベース設計で非常によく登場するのが、1対多です。

1対多とは、

ある1件のデータに対して、別のテーブルのデータが複数件対応する関係

です。

ECサイトで考えてみましょう。

1人のユーザーは、1回だけではなく何度も注文できます。

そのため、

ユーザー
   │
   │ 1
   │
   │ N
 注文

という関係になります。

ここで、

1

は1件、

N

は複数件を表しています。

つまり、

1人のユーザー
      │
      ├─ 注文1
      ├─ 注文2
      └─ 注文3

という関係です。

テーブルでは、

users
-------------------
id
name
email
created_at
orders
-------------------
id
user_id
ordered_at
total_amount

と表現できます。

例えば、山田太郎さんのIDが「1」で、

id name
1 山田太郎

だったとします。

山田さんが3回注文すれば、

id user_id ordered_at
101 1 2026-09-01
102 1 2026-09-05
103 1 2026-09-10

のようになります。

すべての注文が、

user_id = 1

によって山田さんを参照しています。

これが1対多の基本的な形です。


親テーブルと子テーブル

このような関係を説明するとき、

  • 親テーブル
  • 子テーブル

という言葉が使われることがあります。

先ほどの例では、

users
   │
   │ 1
   │
   │ N
orders

となっています。

orders.user_idからusers.idを参照しているため、この関係について考えると、

users  → 親テーブル
orders → 子テーブル

と呼ぶことができます。

イメージとしては、

親:users

ユーザー1
   │
   ├─ 注文101
   ├─ 注文102
   └─ 注文103

子:orders

という形です。

親・子はテーブルに固定された役割ではない

ただし、

usersは常に親テーブル
ordersは常に子テーブル

というわけではありません。

例えば、

users
   │
orders
   │
order_items

という関係があった場合、

ordersはusersとの関係では子ですが、

order_itemsとの関係では親になります。

つまり、

親テーブル・子テーブルという呼び方は、どの参照関係について話しているかによって変わる

ということです。


外部キーは基本的に「多」の側に置く

1対多の関係を設計するとき、

「どちらのテーブルに相手のIDを持たせればよいのか?」

と迷うことがあります。

基本的には、

「多」の側から「1」の側を参照する

と考えると分かりやすいです。

例えば、

ユーザー 1
     │
     │
     N
   注文

であれば、「多」の側であるordersに、

user_id

を持たせます。

users
-------------------
id
orders
-------------------
id
user_id  ← users.idを参照

注文側から、

「この注文は、どのユーザーのものなのか」

を表しているわけです。


多対多

次に、多対多です。

多対多とは、

一方の複数のデータと、もう一方の複数のデータが互いに関連する関係

です。

ECサイトの、

注文
商品

を考えてみましょう。

1つの注文には、複数の商品を含めることができます。

例えば、

注文101
   ├─ パソコン
   ├─ マウス
   └─ キーボード

という注文ができます。

一方、同じ商品が別々の注文に含まれることもあります。

マウス
   ├─ 注文101
   ├─ 注文105
   └─ 注文110

つまり、

1つの注文 → 複数の商品

1つの商品 → 複数の注文

という関係です。

概念的には、

注文 N
   │
   │
   │
   │ N
 商品

となり、これが多対多です。


多対多では中間テーブルを使う

多対多の関係をリレーショナルデータベースで表現するときは、

中間テーブル

を使うのが一般的です。

今回の例では、

order_items

という「注文商品」を管理するテーブルを間に作ります。

 注文
   │
   │ 1
   │
   │ N
注文商品
   │
   │ N
   │
   │ 1
 商品

英語のテーブル名で表すと、

orders
   │
   │ 1
   │
   │ N
order_items
   │
   │ N
   │
   │ 1
products

となります。

例えば、

orders
-------------------
id
user_id
ordered_at
order_items
-------------------
id
order_id
product_id
quantity
products
-------------------
id
name
price

というテーブルを作ります。

ここでorder_itemsには、

order_id
product_id

という2つの参照を持たせています。


中間テーブルのデータを見てみる

例えば、商品が次のように登録されているとします。

id name
201 パソコン
202 マウス
203 キーボード

そして、注文ID「101」で、

  • パソコンを1個
  • マウスを2個

購入したとします。

order_itemsは、例えば次のようになります。

id order_id product_id quantity
1 101 201 1
2 101 202 2

1行目は、

order_id   = 101
product_id = 201

なので、

注文101に商品201(パソコン)が含まれている

ことを表します。

2行目は、

order_id   = 101
product_id = 202

なので、

注文101に商品202(マウス)が含まれている

ことを表しています。

つまり、中間テーブルは、

「どのデータと、どのデータが関連しているのか」

を管理する役割を持っています。


多対多は「2つの1対多」と考える

多対多は、最初は少し理解しにくいかもしれません。

その場合は、

2つの1対多に分解する

と考えると分かりやすくなります。

まず、

注文
   │ 1
   │
   │ N
注文商品

です。

1つの注文には、複数の注文商品が存在します。

次に、

商品
   │ 1
   │
   │ N
注文商品

です。

1つの商品は、複数の注文商品から参照されます。

つまり、

注文 N ─ N 商品

という多対多の関係を、

注文 1 ─ N 注文商品
商品 1 ─ N 注文商品

という2つの1対多に分解しているわけです。

多対多の設計で迷ったときは、

間に中間テーブルを置き、2つの1対多として考える

と理解すると分かりやすいでしょう。


中間テーブルには「関係に関する情報」も持たせられる

中間テーブルには、2つのIDだけではなく、

その関係そのものについての情報

を持たせることもできます。

例えば、

order_items
-------------------
id
order_id
product_id
quantity
unit_price

とします。

quantityは、

「この注文では、この商品を何個購入したのか」

という情報です。

例えば、

注文101 × マウス → 2個
注文102 × マウス → 1個

のように、同じ商品でも注文によって数量が変わります。

この数量は、

orders

だけの情報でも、

products

だけの情報でもありません。

「注文と商品の組み合わせ」

に対する情報です。

そのため、中間テーブルであるorder_itemsに持たせるのが自然です。

unit_priceについても、例えば購入時点の商品単価を保存するのであれば、

「その注文で、その商品をいくらで購入したのか」

という注文商品に関する情報として持たせることができます。

この、

その情報は、何についての情報なのか

という考え方は、テーブル設計では非常に重要です。


1対1・1対多・多対多を整理する

ここまでの内容を一度整理してみましょう。

1対1

ユーザー
   │
   │ 1
   │
   │ 1
プロフィール

1人のユーザーに対して、プロフィールが最大1件対応する関係です。


1対多

ユーザー
   │
   │ 1
   │
   │ N
 注文

1人のユーザーに対して、複数の注文が存在する関係です。

実際のデータベース設計では、非常によく登場します。


多対多

概念的には、

注文 N ─ N 商品

です。

実際には中間テーブルを使い、

 注文
   │ 1
   │
   │ N
注文商品
   │ N
   │
   │ 1
 商品

のように表現します。


「0件でもよいのか」も考える

ここまで、

1対1
1対多
多対多

という分類について説明しました。

しかし、実際の設計ではもう1つ考えておきたいことがあります。

それが、

0件でもよいのか

という点です。

例えば、新しく会員登録したユーザーは、まだ注文をしたことがないかもしれません。

そのため、ユーザーと注文の関係は、より正確には、

1人のユーザーには0件以上の注文が存在する

と考えることができます。

ユーザー
   │
   │
   └─ 注文 0件以上

プロフィールについても、

プロフィール登録は任意

というシステムなら、

1人のユーザーに対してプロフィールは0件または1件

となります。

ER図などでは、

0..1
0..N
1..N

のような表記で最小件数と最大件数を表すことがあります。

最初のうちは、

1対1
1対多
多対多

という基本を理解したうえで、

「0件という状態は許可されるのか?」

もあわせて確認するとよいでしょう。


関係を判断するときは日本語で考える

1対1・1対多・多対多を判断するとき、いきなりテーブル構造を考えると分かりにくい場合があります。

そのようなときは、まず、

業務上の関係を日本語の文章にする

と考えやすくなります。

例えば、ユーザーと注文なら、

1人のユーザーは、複数の注文を行うことができる。

1つの注文は、1人のユーザーに属する。

となります。

つまり、

ユーザー 1 ─ N 注文

です。

注文と商品なら、

1つの注文には、複数の商品を含めることができる。

1つの商品は、複数の注文に含まれることができる。

となります。

両方とも「複数」なので、

注文 N ─ N 商品

となり、多対多であることが分かります。

そこで中間テーブルを作り、

注文
 │
注文商品
 │
商品

という形にします。

迷ったときは、

「AはBをいくつ持てるのか?」

「BはAをいくつ持てるのか?」

という2方向から確認すると、関係を整理しやすくなります。


関係はシステムの要件によって変わる

テーブル同士の関係には、

「このテーブル同士なら必ずこの関係になる」

という決まりがあるわけではありません。

例えば、

ユーザー
住所

について考えてみます。

1ユーザーにつき住所を1件しか登録できないシステムなら、

ユーザー 1 ─ 1 住所

になるかもしれません。

一方、

  • 自宅
  • 勤務先
  • 実家
  • その他の配送先

など、複数の住所を登録できるシステムなら、

ユーザー 1 ─ N 住所

になります。

つまり、同じ「ユーザー」と「住所」であっても、システムの要件によって関係は変わります。

そのためリレーション設計でも、第2回で行った要件整理が重要になります。

実際のシステムでは、データ同士がどのような関係になっているのか

を確認して設計しましょう。


ECサイト全体の関係を見てみる

最後に、今回使用したECサイトのテーブルをまとめてみます。

ユーザー
   │
   │ 1
   │
   │ N
 注文
   │
   │ 1
   │
   │ N
注文商品
   │
   │ N
   │
   │ 1
 商品

テーブル名にすると、

users
   │
   │ 1
   │
   │ N
orders
   │
   │ 1
   │
   │ N
order_items
   │
   │ N
   │
   │ 1
products

そして、主な参照関係は、

users.id
   ↑
   │
orders.user_id
orders.id
   ↑
   │
order_items.order_id
products.id
   ↑
   │
order_items.product_id

となります。

複数のテーブルをこのように関連付けることで、

  • あるユーザーが行った注文
  • ある注文に含まれている商品
  • ある商品が含まれている注文
  • あるユーザーが過去に購入した商品

などをたどれるようになります。

テーブルを1つずつ独立して考えるだけでなく、

システム全体でテーブルがどのようにつながっているのか

を見ることが、リレーション設計では重要です。


まとめ

今回は、テーブル同士の関係を設計する方法について解説しました。

テーブル同士の基本的な関係には、

1対1
1対多
多対多

があります。

1対1は、

ユーザー 1 ─ 1 プロフィール

のように、1件に対して最大1件が対応する関係です。

1対多は、

ユーザー 1 ─ N 注文

のように、1件に対して複数件が対応する関係です。

1対多では基本的に、「多」の側から「1」の側を外部キーで参照します。

多対多は、

注文 N ─ N 商品

のように、双方が複数のデータと関連する関係です。

多対多の関係は、通常は中間テーブルを使って、

注文 1 ─ N 注文商品
商品 1 ─ N 注文商品

という2つの1対多として表現します。

また、テーブル同士を関連付けるときには外部キーを設定することで、

存在しないユーザーを参照する注文

のような不整合を防ぐことができます。

リレーションを考えるときは、

AはBをいくつ持てるのか?

BはAをいくつ持てるのか?

という2方向から考えてみると、関係を整理しやすくなります。

そして何より重要なのは、

実際のシステムや業務では、データ同士がどのような関係になっているのか

を確認することです。

第3回では1つ1つのテーブルやカラムを設計しました。

今回の第4回では、それらのテーブルをつなぎ、システム全体としてどのようなデータ構造にするのかを考えました。

テーブルを設計したあとは、それぞれを独立したものとして見るのではなく、テーブル同士のつながりもあわせて整理していきましょう。


関連記事

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?