我有一些这样的模型:
class Image < ActiveRecord::Base
has_many :tag_groups
end
class TagGroup < ActiveRecord::Base
belongs_to :image
has_many :tag_group_members
has_many :tags, through: :tag_group_members
end
class TagGroupMember < ActiveRecord::Base
belongs_to :tag_group
belongs_to :tag
end
class Tag
has_many :tag_group_members
has_many :tag_groups, through: :tag_group_members
end
我想为图像选择所有标签组及其标签。显而易见的解决方案是:
Image
.includes(tag_groups: :tags)
.find(params[:id])
但是,通过迭代(@image.tag_groups.map{|g| g.tags.map{|t| t.name}}
),我们发现Rails正在进行大量的SQL查询:
TagGroup Load (0.8ms) SELECT "tag_groups".* FROM "tag_groups" WHERE "tag_groups"."image_id" IN (1)
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 LIMIT 1 [["tag_group_id", 1]]
Tag Load (0.3ms) SELECT "tags".* FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 [["tag_group_id", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 LIMIT 1 [["tag_group_id", 3]]
Tag Load (0.3ms) SELECT "tags".* FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 [["tag_group_id", 3]]
Tag Exists (0.6ms) SELECT 1 AS one FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 LIMIT 1 [["tag_group_id", 4]]
Tag Load (0.6ms) SELECT "tags".* FROM "tags" INNER JOIN "tag_group_members" ON "tags"."id" = "tag_group_members"."tag_id" WHERE "tag_group_members"."tag_group_id" = $1 [["tag_group_id", 4]]
现在,似乎只需三次查询即可轻松处理:
SELECT images.* FROM images WHERE images.id = $1
然后:
SELECT tag_groups.* FROM tag_groups WHERE tag_groups.image_id = $1
最后:
SELECT tags.*, tag_groups.id AS tag_group_id FROM tags
INNER JOIN tag_group_members ON tag_group_members.tag_id = tags.id
INNER JOIN tag_groups ON tag_groups.id = tag_group_members.tag_group_id
WHERE tag_groups.image_id = $1 -- alternatively, where tag_groups.id IN (results from the previous query)
然后,您可以简单地将标记关联到内存中正确的标记组,这比查询每个标记组的数据库要快得多。
有没有办法让Rails加载这个关联而不进行那么多连接?
答案 0 :(得分:1)
您甚至不需要提到的第二个查询,因为您的加入查询中已经有tag_groups.image_id
:
i = Image.find params[:id]
tgs = i.tag_groups.joins(:tags)
有趣的是,我没有意识到joins
也没有急切加载内存中的关联,似乎您需要在上面的代码中添加.includes(:tags)
以防止任何其他查询正在运行。
此外,这也完成了同样的事情,但只需一步:
i = Image.joins(tag_groups: :tags).includes(tag_groups: :tags).find 1
答案 1 :(得分:0)
只需:
@image = Image.includes(tag_groups: {tag_group_members: :tags}).find(param[:id])
首先从include
致电Image
,然后返回ActiveRecord::Relation
,之后您可以致电find
。
这样就可以了。