一覧表示時にソートしようとしたらエラーが出た
エラーを掘り下げる
ActiveRecord::StatementInvalid: PG::DuplicateAlias: ERROR: table name "*******" specified more than once
読む
ふむふむ、なるほど
PG::DuplicateAlias
ってことはDB(PostgreSQL)側の問題ですね
ググる
specified more than once
でググって見ると
UPDATE文の失敗。
【PostgreSQL】specified more than once
エイリアスがどうのこうの
SQLから何がダメなのか探る
to_sql
メソッドでデバッグする
SELECT
(省略)
FROM
"table_a"
INNER JOIN "table_b" ON "table_b"."id" = "table_a"."table_b_id"
INNER JOIN "table_c" ON "table_c"."id" = "table_b"."table_c_id"
LEFT OUTER JOIN "table_d" ON "table_d"."id" = "table_c"."table_d_id"
LEFT OUTER JOIN "table_d" ON "table_d"."id" = "table_c"."table_d_id"
WHERE
(省略)
ORDER BY
"table_d"."name" ASC
なるほど
2回同じ結合がされてるのにエイリアスがないではないか
ちなみに各テーブルの関係性は
table_a : table_b : table_c : table_d = N : 1 : 1 : 1
rails側の悪いところを探す
問題の実装部分(実際はもっとごちゃごちゃしてるけどわかりやすく整理)
records =
TableA
.eager_load(table_b: { table_c: :table_d })
.merge(TableD.order(:name))
records.merge(
TableC
.left_joins(:table_d)
.merge(TableD.where(省略))
)
修正
修正案1 ソートと絞り込みを一つにまとめる
TableD.where(省略)
をTableD.order(:name)
にくっつけて
Table.where(省略).order(:name)
にする
残念ながら弊社のシステムの仕組みではソートと絞り込みで処理が別れていて
しかもソートする時としない時でeager_load
があったりなかったりするので不採用
修正案2 left_joins
するかどうかの条件分岐をする
ソートするかどうかでleft_joins
するしないかを決める
なんてね、論外論外
書いてみただけ
修正案3 SQL書いて自分で結合のエイリアスを書く
たぶんやろうと思えばいけるよね
やったことないから知らんけど
でもこれもできるとしても無駄な手間がかかる上にダサいコードになりそうで不採用
修正案4 別名でアソシエーションを定義する
ってことだと思う(英語自信ない...)
https://github.com/rails/rails/pull/33066#issuecomment-395945311
PR上で上がってた
試してないけどなんとかなりそう
でもめんどくさそうだからこれもないかな
修正案5 railsのバージョンを上げるか下げるか
実はこれrailsのバグで5.2.0
でだけ起こるので
5.2.1.rc1
以上にするか5.1
に下げれば治るはず
下記PRで修正されてた(5.2.0リリース後だった)
https://github.com/rails/rails/pull/33066
5.2.2
ではエラーにならないことを確認できた
($ bundle update rails
ってやったら5.2.2まで上がっちゃって
バージョン指定してアップデートする方法ってないのかな?)
マイナーアップデートだからそこまでやばくはないだろうけど
バージョン変えるのってそんなホイホイやりたくないので一旦見送り
修正案6 絞り込み部分のleft_joins
をpreload
にする
left_joins
はSQL上でjoinするけど
preload
は別のSQLを流してrailsがいい感じにキャッシュしてくれるやつだから
left outer join
が2回流れなくなる
一番楽な解決法だけど気持ちパフォーマンスが悪い気がする
けど挙げた中で一番簡単かつ後々への影響も小さいかなと
結論
修正案5のrailsのバージョン変更か
修正案6のleft_joins
をpreload
にする
が妥当かな〜〜