データベース

【DB設計】複数テーブルに多対1で紐づくテーブル設計について

複数テーブルに多対1で関係を紐付けしたい場合に、どのように考えたらよいのか?いつも悩んでいるのでメモとして残す。

どんなケースか?

例えば、「いいね機能を複数モデルに対して実装したい」みたいなケース。

もう少し具体的にすると、記事投稿サイトの記事だけでなく、画像にもいいねを実装したいケース。

選択肢

大きく4つの対応方法がある。

  1. テーブル分割
  2. ポリモーフィック関連
  3. 共通の親テーブルを作成
  4. 複数の交差テーブルを作成

1. テーブル分割

テーブル分割では、記事テーブルに対して記事いいねテーブルを作り、画像テーブルに対して画像いいねテーブルを作る。

素直な方法だが、複数テーブルの数が増えるとしんどくなる。

2. ポリモーフィック関連

ポリモーフィック関連とは、どのテーブルと紐づくかの情報をカラムに追加する方法。

SQLアンチパターンでも紹介されているように、外部キーの設定ができなくなる。

Railsなどのフレームワークにサポートされていることもあるが、アプリケーション側での制御が複雑であり移植性が悪い。

生のSQLを叩く場面でしんどくなること間違いなし。

3. 共通の親テーブルを作成

記事テーブルも、画像テーブルも、ユーザが投稿するものだから、共通の親として投稿テーブルを作成し、その投稿テーブルといいねテーブルを紐付けるという方法。

4. 複数の交差テーブルを作成

共通のいいねテーブルを作成し、共通のいいねテーブルと、記事テーブル、画像テーブルそれぞれの関連づけテーブルを作る。

「1. テーブル分割」では共通のいいねテーブルが存在しないが、この方法だと共通テーブルが存在する。

判断ロジック

ポリモーフィック関連は、選ぶメリットを感じられないので予選落ち。

あとは、ケースバイケース。

将来処理しなければいけないレコード数や処理方法によって設計するのがよさそう。

モデルとして振る舞いが異なり、永続化するデータも違うものになる場合は、「1. テーブル分割」してしまうのもありだし、
「いいね!」みたいに、永続化データが同じであったほうがいい場合は、「3. 共通の親テーブルを作成」「4. 複数の交差テーブルを作成」を検討する。

「3. 共通の親テーブルを作成」と「4. 複数の交差テーブルを作成」の違いは、中間テーブルの数。

いいね先の対象がたくさん増えていくなら「3. 共通の親テーブル」の方が良さそうだが、結局のところ、投稿テーブルと各テーブルの JOIN まわりのロジックがしんどそう。

なので、「いいね!」するということを 「いいねテーブル」 で表現し、それぞれどの対象にいいねしたかについては、交差テーブルに永続化するっていうのがよさそう。

ここらへんをまだ自分のなかで上手く整理できないのが残課題。

-データベース