LoginSignup
0
0

More than 5 years have passed since last update.

ActiveRecord::StatementInvalid: PG::DuplicateAlias: ....

Posted at

一覧表示時にソートしようとしたらエラーが出た

エラーを掘り下げる

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_joinspreloadにする

left_joinsはSQL上でjoinするけど
preloadは別のSQLを流してrailsがいい感じにキャッシュしてくれるやつだから
left outer joinが2回流れなくなる

一番楽な解決法だけど気持ちパフォーマンスが悪い気がする
けど挙げた中で一番簡単かつ後々への影響も小さいかなと

結論

修正案5のrailsのバージョン変更か
修正案6のleft_joinspreloadにする

が妥当かな〜〜

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0