👻

enum + joinの罠

に公開

事前情報

以下のようなテーブル構成とする

table A
has_many :bs
has_many :cs

table B
belongs_to :a

table C
belongs_to :a
enum sautus: { draft: 0, published: 1 }

テーブルを内部結合するとenumが効かない

以下のように内部結合後Cで検索する。

B.joins(a: :cs).where(cs: { sautus: :published })

これでCが公開状態のBが取得できると思ったが結果は空になる。
発行されるSQLを見ると原因はすぐに分かった。

SELECT bs.*
from bs
INNER JOIN as on as.id = bs.a_id
INNER JOIN cs on cs.a_id = as.id
WHERE cs.status = "published"

これのせいである。
cs.status = "published"
DB上にはintger型で保存されているため本来であればcs.status = 1とされるべきだが文字列がそのまま渡ってしまっている。
enumが内部結合で使用されることを想定していないようで変換されないままSQLが発行されてしまう。

解決策

joins+mergeでやりたいことは実現できる。

B.joins(a: :cs).merge(C.where(status: :published))

Discussion