1対1/1対多/多対多の設計
1対1/1対多/多対多の設計 とは
データベース設計における1対1、1対多、多対多のリレーションを学びます。本レッスンでは、1対1/1対多/多対多の設計 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
なぜ重要か
前レッスンでカーディナリティの基本を学びましたが、実務で最も悩むのは 「具体的にどのパターンを選ぶか」「物理的にどう実装するか」 です。同じ業務でも、1 対 1 として設計するか、1 つの大きなテーブルにまとめるか、選択肢があります。多対多 は 中間テーブル で実装しますが、その中間テーブルにも追加 属性 を持たせるべきか否かなど、判断ポイントは多数あります。本レッスンでは、3 つのパターンそれぞれの 適用条件・物理設計パターン・SQL での操作方法 を体系的に学びます。
3 つのパターン比較
3 つの関連パターンの典型的な使い分けは以下の通りです。
| パターン | 業務例 | 物理設計 | 使いどころ |
|---|---|---|---|
| 1 対 1 | ユーザ ↔ プロフィール詳細 | どちらかに FK + UNIQUE | 列が多すぎる時の分割、または任意拡張 |
| 1 対多 | ユーザ → 注文 | 多側に FK | 最も一般的なパターン |
| 多対多 | 学生 ↔ 講座 | 中間テーブル | 両側から複数指定できる場合 |
ステップごとの動作
下図は 3 つのパターンを 1 枚にまとめた ER図 です。各エンティティの位置と FK の位置に注目してください。
ポイントは次の通りです。
- 1 対 1 は
profiles.user_idを主キーかつ外部キーにしてしまうことで、UNIQUE制約を兼ねる 1対多は多側のorders.user_idに FK- 多対多は中間テーブル
enrollmentsに両側の FK を入れ、(student_id, course_id)を複合主キーにする
代表例
1 対多 の SQL
最も頻出のパターン。注文の集計は LEFT JOIN で書きます。
SQL クエリ
CREATE TABLE users (
id INT PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
total INT NOT NULL
);多対多 の SQL
中間テーブル enrollments を経由して JOIN します。「各学生が履修している講座数」を出すクエリは次のように書きます。
SQL クエリ
SELECT s.name, COUNT(e.course_id) AS course_count
FROM students s
LEFT JOIN enrollments e ON e.student_id = s.id
GROUP BY s.id, s.name
ORDER BY s.id;多対多 + 関連属性
中間テーブルに追加属性を持たせることもよくあります。たとえば「履修日」「成績」など、関連自体の属性です。これは中間テーブルが 独自のエンティティに昇格 したと考えると分かりやすいです。
SQL クエリ
CREATE TABLE enrollments (
student_id INT NOT NULL,
course_id INT NOT NULL,
enrolled_at DATE NOT NULL,
grade TEXT,
PRIMARY KEY (student_id, course_id)
);1 対 1 の使いどころ
1 対 1 は意外と使い道が限られます。多くの場合「同じテーブルにまとめてもよい」ためです。それでも 1 対 1 を選ぶ典型ケースは次の通りです。
- 任意の拡張情報 — プロフィールの追加項目(プロフィール画像、SNS リンクなど)
- 大きな列の分離 — BLOB / TEXT 型の本文を別テーブルにして、メイン検索を速くする
- 権限分離 — 機密情報(マイナンバー等)を別テーブルにして、別の権限で守る
よくある落とし穴
多対多の中間テーブルにも主キーを設計する。中間テーブルでは
(left_id, right_id)を複合主キーにすると、同じペアが二重登録されない保証になります。サロゲートキーだけだと、複数登録されても気づきません。
「1 対多」と思っていた関係が、業務拡張で「多対多」になる場合がある。例: 当初は「1 つの注文は 1 つの配送先」だったが、複数配送先対応で「1 注文に複数配送先」になる。設計段階で「将来の変化」も少しは考慮しておくと、後の手戻りが減ります。
まとめ
- 1 対 1 はあまり使わず、使う時は分離理由(拡張・分離・権限)を明確にする
- 1 対多が最も一般的で、多側に FK を置く
- 多対多は中間テーブル + 複合主キーで実装する
- 中間テーブルに属性が付くなら、独自エンティティに昇格させて考える
次のレッスン
次は モデリング基礎クイズ です。データベースモデリングの基礎知識をクイズ形式で確認し、理解を深めます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- 1対1/1対多/多対多 の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. 1対1/1対多/多対多 とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
テーブル構造
schema.sql
CREATE TABLE students (
id INT PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE courses (
id INT PRIMARY KEY,
title TEXT NOT NULL
);
CREATE TABLE enrollments (
student_id INT NOT NULL,
course_id INT NOT NULL,
enrolled_at DATE NOT NULL,
grade TEXT,
PRIMARY KEY (student_id, course_id)
);
INSERT INTO students (id, name) VALUES
(1, '田中'),
(2, '鈴木'),
(3, '佐藤');
INSERT INTO courses (id, title) VALUES
(1, 'データベース基礎'),
(2, 'アルゴリズム'),
(3, 'Web開発');
INSERT INTO enrollments (student_id, course_id, enrolled_at, grade) VALUES
(1, 1, '2026-04-01', 'A'),
(1, 2, '2026-04-02', 'B'),
(1, 3, '2026-04-03', 'A'),
(2, 1, '2026-04-01', 'B'),
(2, 2, '2026-04-02', 'C'),
(3, 3, '2026-04-03', NULL);期待される出力
| student_name | course_title | grade |
|---|---|---|
| 田中 | データベース基礎 | A |
| 田中 | アルゴリズム | B |
| 田中 | Web開発 | A |
| 鈴木 | データベース基礎 | B |
| 鈴木 | アルゴリズム | C |
| 佐藤 | Web開発 | NULL |