複数のテーブルに対し、多対1でテーブルが関係付くときのテーブル設計のパターンについてまとめる。
以下のようなケースのデータ設計を例とする。
issues
pullrequests
comments
comments
がissues
、pullrequests
のどちらに対しても多対一で関係付くようなケース。
issues
pullrequests
comments
comments
にtarget_table
とtarget_id
というカラムを追加し、issues
とpullrequests
のどちらに結びつくか判断させようとするテーブル設計。
SQLアンチパターンではアンチパターンの一つとして取り上げられている。
target_id
がtarget_table
を見ないとissues
とpullrequests
のどちらに関連付くかわからないため、外部キー制約が使えない。
したがって、このパターンではテーブル間の整合性保持はアプリケーションのロジックに依存することなる。
LaravelやRailsのORMではポリモーフィック関連がサポートされているので実装が楽なので、このようなパターンを検討する余地はゼロではないが、なるべく避けたいパターンではある。
issues
pullrequests
issues_comments
pullrequests_comments
comments
issues
とpullrequests
に交差テーブルを用意して、外部キー制約を使えるようにするパターン。
issues
とissues_comments
は1対多、issues_comments
とcomments
は多対1となる。pullrequests
に関しても同様。
アプリケーションの要件次第ではあるが、1コメントがissues
とpullrequests
のどちらかだけに関連付くようにという制約を保証できない。
外部キーが使えるため、ポリモーフィック関連よりは整合性を保つことができる。
issues
pullrequests
posts
comments
issues
、pullrequests
、comments
の共通の親となるテーブルを用意するパターン。
posts
はクラステーブル継承の考え方に基づいて定義するのが良さそう。(要は基底クラスと考える)
(参考:単一テーブル継承・クラステーブル継承・具象クラス継承について
PofEAA)
issues
とposts
が1対1、posts
とcomments
が1対多で関連付く。pull_requests
も同様。posts
とcomments
は1対多で関連付く。
1コメントは1postsに関連付くという制約を保証できるが、issues
とpullrequests
のどちらかだけに関連付くという制約を保証できない。
issues
pullrequests
issue_comments
pullrequest_comments
これはそもそもの前提を疑う話ではあるが、comments
を1つのテーブルにまとめておくのではなく、別々のcomments
テーブルをそれぞれ用意してテーブルを分割しておけば良いのではないかというパターンである。
アプリケーション側のロジックに依存することは、ヒューマンエラーの可能性を高めるので、テーブル構造にロジックを依存させる設計方針が基本的には良いパターンではないかと思う。
アプリケーションの要件に加えて、クエリの気持ちを考えて最適なパターンを選択できるようにしたい。
関連書籍