使用ActiveRecord的include方法时,避免SQL的JOIN?

时间:2014-01-08 02:59:07

标签: ruby-on-rails ruby postgresql activerecord

我有这个ruby代码导致postgresql引发column "urls.id" must appear in the GROUP BY

Song.
  joins(:artist).
  references(:artist).
  where("artists.active = ?", true).
  group("songs.id").
  includes(:urls)

问题是在添加includes(:urls)而不是运行单独的查询时,rails正在加入。是否可以强制rails运行第二个查询以避免此问题?

换句话说;我希望rails在使用JOIN时使用SQL ActiveRecord::Relation.joins,在使用ActiveRecord::Relation.includes时使用单独的查询。

删除wherereferences方法会使所有内容都通过,但我无法查询艺术家表。

错误消息。

SELECT "songs"."id" AS t0_r0, "songs"."artist_id" AS t0_r1, "songs"."title" AS t0_r2, "songs"."grade" AS t0_r3, "songs"."length" AS t0_r4, "songs"."gigs_count" AS t0_r5, "songs"."clicks" AS t0_r6, "songs"."album_cover_id" AS t0_r7, "songs"."created_at" AS t0_r8, "songs"."updated_at" AS t0_r9, "songs"."position" AS t0_r10, "songs"."services" AS t0_r11, "songs"."moved_id" AS t0_r12, "songs"."details_updated_at" AS t0_r13, "songs"."genres_updated_at" AS t0_r14, "urls"."id" AS t1_r0, "urls"."url" AS t1_r1, "urls"."service_id" AS t1_r2, "urls"."media_id" AS t1_r3, "urls"."media_type" AS t1_r4, "urls"."extra" AS t1_r5 FROM "songs" INNER JOIN "artists" ON "artists"."id" = "songs"."artist_id" LEFT OUTER JOIN "url_bridges" ON "url_bridges"."media_bridge_id" = "songs"."id" AND "url_bridges"."media_bridge_type" = 'Song' LEFT OUTER JOIN "urls" ON "urls"."id" = "url_bridges"."url_id" WHERE (artists.active = 't') AND "songs"."id" IN (944) GROUP BY songs.id
PG::GroupingError: ERROR:  column "urls.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: ...AS t0_r13, "songs"."genres_updated_at" AS t0_r14, "urls"."id...
                                                             ^
: SELECT "songs"."id" AS t0_r0, "songs"."artist_id" AS t0_r1, "songs"."title" AS t0_r2, "songs"."grade" AS t0_r3, "songs"."length" AS t0_r4, "songs"."gigs_count" AS t0_r5, "songs"."clicks" AS t0_r6, "songs"."album_cover_id" AS t0_r7, "songs"."created_at" AS t0_r8, "songs"."updated_at" AS t0_r9, "songs"."position" AS t0_r10, "songs"."services" AS t0_r11, "songs"."moved_id" AS t0_r12, "songs"."details_updated_at" AS t0_r13, "songs"."genres_updated_at" AS t0_r14, "urls"."id" AS t1_r0, "urls"."url" AS t1_r1, "urls"."service_id" AS t1_r2, "urls"."media_id" AS t1_r3, "urls"."media_type" AS t1_r4, "urls"."extra" AS t1_r5 FROM "songs" INNER JOIN "artists" ON "artists"."id" = "songs"."artist_id" LEFT OUTER JOIN "url_bridges" ON "url_bridges"."media_bridge_id" = "songs"."id" AND "url_bridges"."media_bridge_type" = 'Song' LEFT OUTER JOIN "urls" ON "urls"."id" = "url_bridges"."url_id" WHERE (artists.active = 't') AND "songs"."id" IN (944) GROUP BY songs.id

我在x86_64-unknown-linux-gnu上运行PostgreSQL 9.3.2,由gcc-4.4.real(Ubuntu 4.4.3-4ubuntu5)4.4.3,64位编译。

2 个答案:

答案 0 :(得分:5)

includes替换为preload,文档内容不够明确,但会运行第二个查询以急切加载您指定的关系。我已将其用于与joinsgroup相同的用例。

Song.
  joins(:artist).
  references(:artist).
  where("artists.active = ?", true).
  group("songs.id").
  preload(:urls)

http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-preload

答案 1 :(得分:0)

我建议手动第二次查询网址。不要使用包含(:urls)。

@songs = Song.complex_query
@urls = Url.where(:song_id => @songs.ids)
@urls_by_song_id = @urls.group_by {|u| u.song_id}

使用group_by会将url放入哈希,其中键为song_id,值为url数组。您可以访问这样的网址:

@urls_by_song_id[song.id]

它不像包含一样干净,但它具有相同的效果。这使您可以访问没有n + 1开销的URL。