複数テーブルに多対1で関係を紐付けしたい場合に、どのように考えたらよいのか?いつも悩んでいるのでメモとして残す。
どんなケースか?
例えば、「いいね機能を複数モデルに対して実装したい」みたいなケース。
もう少し具体的にすると、記事投稿サイトの記事だけでなく、画像にもいいねを実装したいケース。
選択肢
大きく4つの対応方法がある。
- テーブル分割
- ポリモーフィック関連
- 共通の親テーブルを作成
- 複数の交差テーブルを作成
1. テーブル分割
テーブル分割では、記事テーブルに対して記事いいねテーブルを作り、画像テーブルに対して画像いいねテーブルを作る。
素直な方法だが、複数テーブルの数が増えるとしんどくなる。
2. ポリモーフィック関連
ポリモーフィック関連とは、どのテーブルと紐づくかの情報をカラムに追加する方法。
SQLアンチパターンでも紹介されているように、外部キーの設定ができなくなる。
Railsなどのフレームワークにサポートされていることもあるが、アプリケーション側での制御が複雑であり移植性が悪い。
生のSQLを叩く場面でしんどくなること間違いなし。
3. 共通の親テーブルを作成
記事テーブルも、画像テーブルも、ユーザが投稿するものだから、共通の親として投稿テーブルを作成し、その投稿テーブルといいねテーブルを紐付けるという方法。
4. 複数の交差テーブルを作成
共通のいいねテーブルを作成し、共通のいいねテーブルと、記事テーブル、画像テーブルそれぞれの関連づけテーブルを作る。
「1. テーブル分割」では共通のいいねテーブルが存在しないが、この方法だと共通テーブルが存在する。
判断ロジック
ポリモーフィック関連は、選ぶメリットを感じられないので予選落ち。
あとは、ケースバイケース。
将来処理しなければいけないレコード数や処理方法によって設計するのがよさそう。
モデルとして振る舞いが異なり、永続化するデータも違うものになる場合は、「1. テーブル分割」してしまうのもありだし、
「いいね!」みたいに、永続化データが同じであったほうがいい場合は、「3. 共通の親テーブルを作成」「4. 複数の交差テーブルを作成」を検討する。
「3. 共通の親テーブルを作成」と「4. 複数の交差テーブルを作成」の違いは、中間テーブルの数。
いいね先の対象がたくさん増えていくなら「3. 共通の親テーブル」の方が良さそうだが、結局のところ、投稿テーブルと各テーブルの JOIN まわりのロジックがしんどそう。
なので、「いいね!」するということを 「いいねテーブル」 で表現し、それぞれどの対象にいいねしたかについては、交差テーブルに永続化するっていうのがよさそう。
ここらへんをまだ自分のなかで上手く整理できないのが残課題。