
データベース設計を進めるなかで、「学生と授業」「商品と注文」のように、双方が複数の相手と結びつく関係に必ず出会います。
このような関係は多対多と呼ばれ、リレーショナルデータベースではそのままの形でテーブルに実装できないという特徴を持つため、中間テーブルを使った設計が必要です。
本記事では、多対多の基本概念から中間テーブルを使った設計手順5ステップ、実務で頻発する失敗パターンまでを具体例とあわせて解説しますので、テーブル設計の実践にぜひお役立てください。
目次
多対多とは?テーブル間の関係性を表すリレーションの一種
まず、多対多という言葉が指す関係性そのものを正しく理解することが、適切なテーブル設計の出発点になります。ここでは多対多の定義を確認したうえで、1対1・1対多との違いや身近な具体例を順に見ていきます。
多対多の定義:双方のレコードが互いに複数の相手と関連する状態
多対多(Many-to-Many)とは、2つのテーブルの間で、双方のレコードが互いに複数の相手と関連しうる状態を指すリレーションの一種です。たとえばテーブルAの1件がテーブルBの複数件と結びつき、同時にテーブルBの1件もテーブルAの複数件と結びつく場合、この2つは多対多の関係にあります。
実務のデータモデリングでは、多対多はごく普通に登場する関係であり、珍しいものではありません。重要なのは、多対多を発見したときに「どう実装に落とし込むか」をパターンとして知っておくことです。リレーションの種類全体を体系的に整理したい方は、以下の記事もあわせてご覧ください。
データリレーションシップとは?データベース設計で押さえるべき種類と実務での活用方法
1対1・1対多との違い
テーブル間のリレーションには、多対多のほかに1対1と1対多があります。3つの違いを整理すると次の表のとおりです。
関係 | 意味 | 例 | 実装方法 |
|---|---|---|---|
1対1 | 一方の1件が他方の1件とだけ対応する | 社員と社員詳細情報 | 外部キーまたはテーブル統合 |
1対多 | 一方の1件が他方の複数件と対応する | 部署と社員 | 「多」側に外部キーを持たせる |
多対多 | 双方の複数件が互いに対応する | 学生と授業 | 中間テーブルで分解する |
1対多であれば「多」側のテーブルに外部キーを1つ追加するだけで表現できますが、多対多だけは外部キーの追加だけでは実装できません。この「実装方法が根本的に異なる」という点こそが、多対多を特別扱いして学ぶ理由です。データベース全体の基礎から確認したい方は、以下の記事が参考になります。
データベースとは?種類・仕組み・活用事例まで基礎知識を徹底解説
多対多の身近な例:学生と授業・商品と注文
多対多のイメージをつかむには、身近な例で考えるのが近道です。代表的なのは学生と授業の関係で、1人の学生は複数の授業を履修し、1つの授業には複数の学生が参加します。
ECサイトの商品と注文も同じ構造です。1回の注文には複数の商品が含まれ、1つの商品は多くの注文に登場します。このように多対多は特殊なケースではなく、業務データのいたるところに潜んでいる関係だといえます。
多対多の関係をそのまま実装できない理由
多対多が実装できない理由を理解しておくと、中間テーブルの必要性が腹落ちしやすくなります。ここでは、外部キーによる表現の限界と、カラム追加による対応が抱える構造的な問題を順に解説します。
外部キー1つでは複数の関連を表現できないため
1対多の関係は、「多」側のテーブルに外部キーを1本持たせることで表現できます。しかし多対多では、どちらのテーブルに外部キーを置いても、1つのカラムには1つの値しか格納できないため、複数の関連を表現しきれません。
たとえば学生テーブルに授業IDのカラムを設けると、その学生は1つの授業しか履修できなくなります。逆に授業テーブルに学生IDを持たせても、1つの授業に1人しか登録できない構造になってしまうのです。
カラム追加で対応すると変更に弱い構造になるため
「授業ID1、授業ID2、授業ID3」のようにカラムを増やして対応する方法を思いつくかもしれませんが、これは典型的なアンチパターンです。履修数が上限を超えるたびにテーブル定義の変更が必要になり、仕様変更に極めて弱い構造になります。
さらに、「この学生が履修している授業を探す」ためにすべてのカラムを検索しなければならず、SQLも複雑化します。空のカラムが大量に生まれてストレージ効率も悪化するため、カラム追加による対応は避けるべきです。
多対多を解消する中間テーブルの仕組み
多対多を実装するための標準的な解決策が中間テーブルです。ここでは中間テーブルの構造と、それによって多対多がどのように管理可能になるのかを3つの観点から説明します。
中間テーブルとは:2つのテーブルの外部キーを持つ橋渡し役のテーブル
中間テーブルとは、関連させたい2つのテーブルの主キーを外部キーとして持つ、橋渡し役のテーブルです。学生と授業の例であれば、学生IDと授業IDの2つのカラムを持つ「履修」テーブルがこれにあたります。
中間テーブルの1レコードは「どの学生が、どの授業を履修しているか」という関連1件を表します。関連付けの情報をデータとして持つ専用のテーブルを用意する、というのが中間テーブルの本質です。
多対多を2つの1対多に分解して管理できる
中間テーブルを挟むと、「学生 対 履修」が1対多、「授業 対 履修」も1対多となり、多対多の関係を2つの1対多に分解できます。1対多はリレーショナルデータベースが最も得意とする形であるため、既存の仕組みだけで安全に管理できるようになります。
この「多対多は2つの1対多に分解する」という考え方は、テーブル設計における定石中の定石です。ER図の上でも、多対多の線を見つけたら中間にエンティティを1つ追加する、という機械的な手順で解消できます。
関連の追加・削除にレコード操作だけで対応できる
中間テーブル方式の大きな利点は、関連の追加・削除がレコードのINSERT・DELETEだけで完結することです。学生が新しい授業を履修すれば履修テーブルに1行追加し、履修を取りやめれば該当行を削除するだけで済みます。
テーブル定義の変更(ALTER TABLE)が不要なため、運用中のシステムでも安全に関連を増減できます。関連数に上限がなく、将来の拡張にも柔軟に対応できる点が、カラム追加方式との決定的な違いです。
中間テーブルを使った多対多の設計手順5ステップ
ここからは、実際に多対多を中間テーブルで設計するための手順を5つのステップに分けて解説します。要件の整理からSQLでの動作確認まで、実務の流れに沿って進めていきましょう。
STEP1:エンティティ間の関係性を洗い出す
最初に、システムで扱うエンティティ(管理対象)を洗い出し、それぞれの間にどのような関係があるかを整理します。「学生」「授業」「教員」のように名詞で抽出し、業務のルールを確認しながら関係の種類を判断していくのが基本です。
このとき、「1人の学生は授業をいくつ履修できるか」「1つの授業には何人参加するか」といった質問を業務担当者に投げかけると、関係の多重度を正確に把握できます。エンティティの考え方そのものを整理したい方は、以下の記事をご参照ください。
エンティティとは?データベース設計とデータモデリングの基本概念
STEP2:多対多になっている箇所を特定する
洗い出した関係のなかから、双方向に「複数」となっている箇所を特定します。「AはBを複数持つか」「BはAを複数持つか」という2つの問いが両方ともYESであれば、そこが多対多です。
特定した多対多はER図に明示しておくと、チーム内での認識合わせに役立ちます。ER図の書き方や表記ルールについては、以下の記事で詳しく解説しています。
ER図とは?基本ルールから書き方まで徹底解説!
STEP3:中間テーブルを作成し外部キーを設定する
多対多と判明した箇所に中間テーブルを作成し、両側のテーブルの主キーを参照する外部キーを2本設定します。外部キーには参照整合性制約を付与し、存在しない学生IDや授業IDが登録されない状態を担保するのが重要なポイントです。
削除時の挙動(CASCADE・RESTRICTなど)もこの段階で決めておきます。たとえば学生が退学した場合に履修レコードを自動削除するのか、履修が残っている限り削除を禁止するのかは、業務要件によって判断が分かれるところです。
STEP4:主キーの構成を決める
中間テーブルの主キーには、2つの外部キーを組み合わせた複合主キーを使う方法と、連番のIDを別途用意するサロゲートキーを使う方法があります。複合主キーを採用すると、同じ組み合わせの重複登録をデータベースの仕組みで防げます。
一方、ORMとの相性や他テーブルからの参照しやすさを重視するなら、サロゲートキーが有力な選択肢です。どちらを選ぶ場合でも、組み合わせの一意性はUNIQUE制約などで必ず担保しておきましょう。
STEP5:SQLでテーブルを作成しJOINで動作を確認する
最後に、SQLでテーブルを実際に作成し、JOINで意図どおりにデータを取り出せるか確認します。学生と授業の例では、次のようなDDLになります。
CREATE TABLE 履修 (
学生ID INT NOT NULL,
授業ID INT NOT NULL,
PRIMARY KEY (学生ID, 授業ID),
FOREIGN KEY (学生ID) REFERENCES 学生(学生ID),
FOREIGN KEY (授業ID) REFERENCES 授業(授業ID)
);
作成後は、学生・履修・授業の3テーブルをJOINするクエリを実行し、「ある学生の履修授業一覧」「ある授業の受講者一覧」が正しく取得できるかを確認します。SQLの基本文法から復習したい方は、以下の記事もご覧ください。
SQLとは?データベースの操作言語
多対多のテーブル設計で押さえたい実務のポイント
中間テーブルの仕組み自体はシンプルですが、実務では設計品質を左右する細かな判断が数多く発生します。ここでは、現場のプロジェクトで特に効果の大きい4つのポイントを紹介します。
中間テーブルの命名規則をプロジェクト内で統一する
中間テーブルの名前は、プロジェクト内でルールを統一しておくことが重要です。命名が統一されていないと、テーブル一覧を見ただけでは中間テーブルかどうか判別できず、設計の意図が後任者に伝わりません。
実務でよく使われる命名パターンには、次のようなものがあります。
- 2つのテーブル名を連結する(例:学生授業、user_course)
- 関係そのものを表す業務用語を使う(例:履修、注文明細、割当)
- 接尾辞で中間テーブルであることを示す(例:〜_map、〜_rel)
このなかでは、「履修」「注文明細」のように業務上の意味を持つ名前を付けられるケースが最も望ましいといえます。名前が業務用語と一致していると、設計書やソースコードの可読性が大きく向上するためです。
関係そのものが持つ属性は中間テーブルに持たせる
「履修した日付」「注文した数量」のように、どちらのエンティティ単体にも属さず、関係そのものに付随する情報は中間テーブルのカラムとして持たせます。これは学生の属性でも授業の属性でもなく、「学生が授業を履修した」という事実に紐づく情報だからです。
関係の属性を正しく中間テーブルに配置できると、データの重複や更新漏れを構造的に防げます。属性(アトリビュート)の考え方については、以下の記事で詳しく整理しています。
アトリビュートとは?データベース設計における意味と使い方をわかりやすく解説
複合主キーとサロゲートキーを要件に応じて使い分ける
主キーの構成は、要件に応じて複合主キーとサロゲートキーを使い分けるのが実務的な判断です。それぞれの特徴を比較すると次のとおりです。
観点 | 複合主キー | サロゲートキー |
|---|---|---|
構成 | 外部キー2つの組み合わせ | 連番などの独立したID |
重複防止 | 主キー制約で自動的に防げる | 別途UNIQUE制約が必要 |
他テーブルからの参照 | キーが2カラムになり煩雑 | 1カラムで参照できる |
ORMとの相性 | 対応していないものがある | ほとんどのORMで扱いやすい |
同じ組み合わせの再登録を許して履歴を残す要件がある場合は、複合主キーでは対応できないため、サロゲートキーを選択します。一方で、同じ組み合わせを二重に登録したくない通常の要件であれば、複合主キーのシンプルさが活きるでしょう。プロジェクトのORMや既存テーブルの方針も踏まえて、チームとしての基準を決めておくことが大切です。
正規化の観点から設計全体を見直す
中間テーブルの導入は、正規化の考え方とも密接に関係しています。1つのカラムに複数の値を持たせないという第1正規形の原則を守ること自体が、多対多をカンマ区切りで実装してはいけない理由の裏付けです。
中間テーブルを設計したあとは、ほかのテーブルも含めて正規化の観点で全体を見直すと、冗長なカラムや隠れた依存関係を発見できます。データモデリング全体の進め方は、以下の記事で解説しています。
データモデリングとは?その重要性と役割、手法を解説
多対多の設計でよくある失敗パターン
多対多の設計には、経験の浅いエンジニアが高い確率で踏んでしまう定番の失敗パターンがあります。ここでは実務で特によく見かける4つの失敗を、原因と対策とあわせて紹介します。
1つのカラムに複数のIDをカンマ区切りで格納してしまう
最も多い失敗が、1つのカラムに「1,3,5」のように複数のIDをカンマ区切りで格納してしまうパターンです。一見手軽に見えますが、この方法では検索・集計・整合性チェックのすべてが困難になり、後々の開発コストが跳ね上がります。
カンマ区切りのカラムはインデックスが効かず、外部キー制約も設定できないため、存在しないIDが混入しても検知できません。すでにこの形で運用されている場合は、早い段階で中間テーブルへの移行を計画することをおすすめします。
カラムを増やして関連の数に対応しようとする
「授業ID1」「授業ID2」のようにカラムを増やす方法も、カンマ区切りと並ぶ典型的な失敗です。上限を超えるたびにALTER TABLEとアプリケーション改修が必要になり、変更のたびにリリース作業が発生します。
またこの構造では、特定のIDを含むレコードを探すのにすべてのカラムをOR条件で検索する必要があり、クエリの複雑化とパフォーマンス低下を招きます。関連の数が可変である以上、レコード追加で柔軟に伸ばせる中間テーブル方式のほうが優れた構造です。
隠れた多対多の関係を見落とし後から大規模な改修が発生する
設計当初は1対多に見えていた関係が、実は多対多だったと後から判明するケースも少なくありません。たとえば「社員は1つの部署に所属する」と決めていたのに、兼務の要件が追加されて社員と部署が多対多になる、といった事例は非常によくあります。
運用開始後に多対多へ変更するには、テーブル構造の変更に加えてアプリケーション側の大規模な改修が必要になります。設計段階で「将来この関係が複数になる可能性はないか」を業務担当者に確認しておくことが、最も費用対効果の高いリスク対策です。
中間テーブルに関係のない属性を詰め込んでしまう
中間テーブルに、関係とは無関係な属性を詰め込んでしまう失敗もよく見られます。たとえば履修テーブルに学生の住所や授業の教室名を持たせると、同じ情報が複数レコードに重複して保存され、更新漏れによるデータ不整合の温床になります。
中間テーブルに置いてよいのは、あくまで「関係そのものに付随する属性」だけです。各カラムについて「これは誰の情報か」を自問し、エンティティ本体の属性は元のテーブルに戻すことを徹底しましょう。
多対多の設計イメージがわかる活用例
最後に、中間テーブルを使った多対多の設計が実際のシステムでどのように使われているかを、3つの事例で確認します。いずれも定番の構成のため、自社システムの設計にそのまま応用できます。
ECサイト:商品と注文を注文明細テーブルで管理する
ECサイトでは、商品と注文が多対多の関係になります。この2つを橋渡しするのが注文明細テーブルで、注文ID・商品IDの外部キーに加えて、数量や購入時単価といった関係の属性を保持するのが一般的です。
購入時単価を注文明細に持たせるのは、商品マスタの価格が後から改定されても、注文当時の金額を正しく残すためです。マスタと明細を分けて管理する考え方は、以下の記事でも詳しく解説しています。
マスタデータとは?具体例でクイックに解説!
学習管理システム:受講者と講座を受講登録テーブルで管理する
学習管理システム(LMS)では、受講者と講座の多対多を受講登録テーブルで管理します。受講登録テーブルには、申込日・進捗率・修了フラグといった学習状況の属性を持たせるのが定番の設計です。
この構造にしておくと、「受講者ごとの受講講座一覧」と「講座ごとの受講者一覧」の両方をJOINだけで取得できます。修了証の発行や進捗レポートの作成も、受講登録テーブルを起点に集計するだけで実現可能です。
社内システム:社員とプロジェクトを割当テーブルで管理する
社員とプロジェクトの関係も、兼務や複数案件への参画が当たり前の現在では多対多として設計するのが安全です。割当テーブルに社員IDとプロジェクトIDを持たせ、参画開始日・終了日・役割・稼働率などを関係の属性として管理します。
期間の属性を持たせておくと、「ある時点で誰がどのプロジェクトにいたか」という履歴の照会にも対応できます。人事異動やアサイン変更が頻繁な組織ほど、この設計の恩恵は大きいといえるでしょう。
まとめ:多対多は中間テーブルで2つの1対多に分解して設計する
多対多とは、2つのテーブルの双方のレコードが互いに複数の相手と関連する状態を指し、外部キー1本では実装できないため中間テーブルで2つの1対多に分解して設計します。この原則さえ押さえれば、学生と授業、商品と注文のようなあらゆる多対多を同じパターンで安全に実装できます。
設計の際は、命名規則の統一・関係の属性の配置・主キー方式の使い分けといった実務のポイントを意識し、カンマ区切りやカラム追加といった失敗パターンを避けることが大切です。本記事の5ステップを参考に、変更に強いテーブル設計を実践してみてください。
「これからデータベース設計やテーブル設計に取り組みたいけれど、何から手をつけたらいいかわからない」「データモデリングの専門家の知見を取り入れたい」という方は、データ領域の実績豊富な弊社、データビズラボにお気軽にご相談ください。
貴社の課題や状況に合わせて、データの取り組みをご提案させていただきます。








