AまたはBを持つレコードのテーブル
「Issue または PullRequest に紐づくコメント」とか「画像または文字を持ったレイヤー」とか、
「〇〇または△△に紐づくテーブル」みたいなことをしたいときの設計はいくつか種類がある。
アプリケーションの型なら Union Type で表すような概念だが、RDB にはそんなものはないのでみんないろんな方法でなんとかする。
悪い方法含めて定番は以下
ポリモーフィック関連
#Rails だとふつうに機能として提供されるので行われやすいやつ 外部キー制約を諦めることになるとかなんとか
table:comments
id commentable_type commentable_id body
1 Issue 1 この Issue ずっと人いなすぎ
2 PullRequest 1 LGTM
#Rails だと少し前に delegated_type という機能が入った(STI の代替) 中身は実はポリモーフィック関連に毛の生えたようなやつ
単一テーブル継承(STI)
コメントに type があるのではなく、コメント対象の方に type がある
分岐されうる対象の方を1テーブルで全部表現する
「A または B というのは嘘。本当は両方とも C の一種」
コメントは C に紐付けることだけ考えればいい
全部のコメント対象に同じカラムが生えてるのって合理的か?あ?みたいなことがある
Issue と PR を同じ「Thread」として扱うとかならまぁありえなくはない?
GitHub は現に両方をまとめて「Issue」と呼んでいる
が、レイヤーに紐づく画像と文字を同じテーブルで扱うとかだと正気じゃない感じになる
LayerBody というテーブルがあって、image_url と font_family と text というカラムが全部生えるとか
ある type のときにしか使わない、nullable なカラムが大量に生える設計を誘発する
table:comments
id thread_id body
1 1 この Issue ずっと人いなすぎ
2 2 LGTM
table:threads
id type title description
1 Issue なんかそういう Issue がある はい
2 PullRequest なんかを直した はい
中間テーブルを1対1で使うやつ
中間テーブルは基本的に多対多で使うものなのでびっくりするやつ
「あとで多対多になるかもしれない」というのに備える意志でやるならアリ
belongs_to_if
type はあってもなくてもいい
issue_id が null じゃなかったら IssueComment、みたいな分岐をアプリケーション側でやっても良い
外部キー制約が貼れる
カラムが増えて嫌な感じもするが経験上一番ラクかつマシな感じになる
type が10種類とかあるといやな気持ちになるが、3種類程度ならメリットのほうが上回りやすい
issue_id と pull_request_id 両方 null とかはアプリケーション側で防ぐしかない、はず
type を not null に指定して、type が Issue ならば仮に pull_request_id がセットされちゃってても無視するとかができる
table:comments
id type issue_id pull_request_id body
1 Issue 1 null この Issue ずっと人いなすぎ
2 PullRequest null 1 LGTM