コントローラーでeachして色々と処理をしている処理がいくつかのコントローラにあったので、リファクタしてみることに。
まず、とあるページでヘルパーに移してみて特に問題なかったので、全ページ対応してみた。するとエラーがでるページもでてきた。
同じように対応したのに、なぜ?って思って調べてたら、遅延評価とany?で発行されるSQLが原因だった。リファクタ前のコードではコントローラーで、load済みになっていたから、any?してもSQLが発行されてなかったけど、ヘルパーに移したせいでloadされていなくてカウントのSQLが発行されるようになってしまったのだ。で、元々のSQLがGROUP BYしてて、ORDER BYで集約関数使った存在しないカラムを指定していたものだから、any?で発行されたカウントのSQLでエラーが発生した。だいたい下記のような感じ。
[1] pry(main)> Tag.select("tags.id,tags.name,count(feeds.id) as count").joins(:feeds).group("tags.id").order("count desc").limit(3)
Tag Load (2.1ms) SELECT tags.id,tags.name,count(feeds.id) as count FROM "tags" INNER JOIN "feed_tags" ON "feed_tags"."tag_id" = "tags"."id" INNER JOIN "feeds" ON "feeds"."id" = "feed_tags"."feed_id" GROUP BY tags.id ORDER BY count desc LIMIT 3
=> [#<Tag id: 11, name: "監獄">,
#<Tag id: 24, name: "がっこう">,
#<Tag id: 3, name: "城下町">]
[2] pry(main)> Tag.select("tags.id,tags.name,count(feeds.id) as count").joins(:feeds).group("tags.id").order("count desc").limit(3).any?
(0.6ms) SELECT COUNT(*) AS count_all, tags.id AS tags_id FROM "tags" INNER JOIN "feed_tags" ON "feed_tags"."tag_id" = "tags"."id" INNER JOIN "feeds" ON "feeds"."id" = "feed_tags"."feed_id" GROUP BY tags.id ORDER BY count desc LIMIT 3
SQLite3::SQLException: no such column: count: SELECT COUNT(*) AS count_all, tags.id AS tags_id FROM "tags" INNER JOIN "feed_tags" ON "feed_tags"."tag_id" = "tags"."id" INNER JOIN "feeds" ON "feeds"."id" = "feed_tags"."feed_id" GROUP BY tags.id ORDER BY count desc LIMIT 3
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: count: SELECT COUNT(*) AS count_all, tags.id AS tags_id FROM "tags" INNER JOIN "feed_tags" ON "feed_tags"."tag_id" = "tags"."id" INNER JOIN "feeds" ON "feeds"."id" = "feed_tags"."feed_id" GROUP BY tags.id ORDER BY count desc LIMIT 3
で、対応。loadしなくなってSQLが発行されるようになってしまったので、一旦配列にloadすることでムダなカウントSQLを発行させなくて済むようになりました。
[3] pry(main)> Tag.select("tags.id,tags.name,count(feeds.id) as count").joins(:feeds).group("tags.id").order("count desc").limit(3).to_a.any?
Tag Load (2.0ms) SELECT tags.id,tags.name,count(feeds.id) as count FROM "tags" INNER JOIN "feed_tags" ON "feed_tags"."tag_id" = "tags"."id" INNER JOIN "feeds" ON "feeds"."id" = "feed_tags"."feed_id" GROUP BY tags.id ORDER BY count desc LIMIT 3
=> true