DBのレッスンで習った「DB設計」の復習です。
多対多の関係のテーブルをどうやって表すのかが難しかったので、備忘録してまとめてみます。
まずは一対多のテーブルの作り方から
「生徒」と「部活」のテーブルを例にして考えます。
生徒は必ず1つだけ部活を選んで所属するという前提条件があったとしたら、部活は複数人の部員を抱えていることになるので、一対多の関係が成り立ちます。
どの生徒がどの部活に所属しているか分かるようにしたい場合、部活のidを生徒に紐づければ良いので、テーブルは以下のようになります。
|Column|Type|Options|
|------|----|-------|
|student|string|null: false, index: true|
|club_id|integer|null: false|
Association
belongs_to :club
|Column|Type|Options|
|------|----|-------|
|club|string|null: false, index: true|
Association
has_many :students
これらをもとにデータを入れるとこんな感じになります。
studentsテーブル
| id | student | club_id |
|---|---|---|
| 1 | 田中 太郎 | 3 |
| 2 | 山田 花子 | 1 |
| 3 | 高橋 順子 | 1 |
clubsテーブル
| id | club |
|---|---|
| 1 | バスケ部 |
| 2 | 野球部 |
| 3 | ハンドボール部 |
この二つのテーブルから、田中太郎さんはハンドボール部に所属していて、山田さんと高橋さんはバスケ部に所属しているというデータをとることができます。
多対多の関係のテーブルの定義の方法
ではもし前提条件が生徒は複数の部活に所属しても良いとなったらどうなるでしょう。
上の生徒のテーブルにカラムを足して以下のようにしたとします。
studentsテーブル
| id | student | club1 | club2 |
|---|---|---|---|
| 1 | 田中 太郎 | 3 | 2 |
| 2 | 山田 花子 | 1 | |
| 3 | 高橋 順子 | 1 | 3 |
| これは良くない例です。なぜなら、このように所属しているクラブを重複して書く書き方は、データベースの設計として間違ってるためです。そこで、中間テーブルを用意する必要があります。 | |||
中間テーブルとは、多対多の関係を表すために用意するテーブルです。 |
|||
| 今回の場合、どの生徒とどの部活が結びつくかを表すテーブルをもう一つ用意すればいいので、そのテーブルをrelationsと名付けます。 |
studentsテーブル
| id | student |
|---|---|
| 1 | 田中 太郎 |
| 2 | 山田 花子 |
| 3 | 高橋 順子 |
clubsテーブル
| id | club |
|---|---|
| 1 | バスケ部 |
| 2 | 野球部 |
| 3 | ハンドボール部 |
relationsテーブル
| id | student | club |
|---|---|---|
| 1 | 1 | 3 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 3 | 3 |
こうすることで、studentカラムやclubカラムを重複することなく、関係を表すことができます。
定義の方法は以下のようになります。
|Column|Type|Options|
|------|----|-------|
|student|string|null: false, index: true|
Association
has_many :relations
has_many :clubs, through: :relations
|Column|Type|Options|
|------|----|-------|
|club|string|null: false, index: true|
Association
has_many :relations
has_many :students, through: :relations
二つのテーブルのAssociationに注目するとさっきまでの定義になかったthrough: :relationsが見られます。これは、中間テーブルを明確に表すために必要な定義です。
また、中間テーブルはthroughの前に定義する必要があります。 これはプログラミングが上から順番に読まれる性質があるためです。
|Column|Type|Options|
|------|----|-------|
|student|references|null: false, foreign_key: true|
|club|references|null: false, foreign_key: true|
### Association
belongs_to :student<br>
belongs_to :club<br>
relationsテーブルでは、指定するカラム名をstudent_idとせずにstudentとし、型はinteger型ではなくreference型にします。これはinteger型だと外部キー制約にならないためです。
Associationはひとつずつの組み合わせを表すためbelongs_toでそれぞれのテーブルを指定します。
まとめ
1.中間テーブルとは多対多の関係の2つのテーブルをつなぐためのテーブル
2.中間テーブルでない2つのテーブルでは、Associationで
thorough: :中間テーブル と記述する必要がある
3.中間テーブルでは、カラム名に_idは省き、型はreference型を指定する。
参考ページ
railsガイド
railsで多対多のアソシエーションの作り方と、出来ること
Railsの外部キー制約とreference型について