首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为了提高效率和优化,我应该使用eager_load还是包含?

为了提高效率和优化,我应该使用eager_load还是包含?
EN

Stack Overflow用户
提问于 2015-10-08 17:48:49
回答 2查看 692关注 0票数 1

目前,我的代码如下所示:

current_user.association.includes(a: [:b, {c: :d}, {e: :f}]).to_a

在执行调用时,似乎每个包含都是通过自己对DB的SELECT调用来调用的。

然而,当我执行current_user.association.eager_load(a: [:b, {c: :d}, {e: :f}]).to_a时,我会看到一个巨大的SELECT调用。

我这么问是因为我以前从没见过这样的事。我认为,由于DB调用较少,eager_load更有效率。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-11-11 17:35:36

由于我无法从您的描述(a: [:b, {c: :d}, {e: :f}])中推断出这个查询,所以我需要稍微谈谈includes

includes是一种适用于不同情况的查询方法。

下面是一些示例代码:

代码语言:javascript
复制
# model and reference
class Blog < ActiveRecord::Base
  has_many :posts

  # t.string   "name"
  # t.string   "author"
end

class Post < ActiveRecord::Base
  belongs_to :blog

  # t.string   "title"
end

# seed
(1..3).each do |b_id|
  blog = Blog.create(name: "Blog #{b_id}", author: 'someone')
  (1..5).each { |p_id| blog.posts.create(title: "Post #{b_id}-#{p_id}") }
end

在一种情况下,它会激发两个独立的查询,就像preload一样。

代码语言:javascript
复制
> Blog.includes(:posts)
  Blog Load (2.8ms)  SELECT "blogs".* FROM "blogs"
  Post Load (0.7ms)  SELECT "posts".* FROM "posts" WHERE "posts"."blog_id" IN (1, 2, 3)

在另一种情况下,当查询引用的表时,它只触发一个LEFT OUTER JOIN查询,就像eager_load一样。

代码语言:javascript
复制
> Blog.includes(:posts).where(posts: {title: 'Post 1-1'})
  SQL (0.3ms)  SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id" WHERE "posts"."title" = ?  [["title", "Post 1-1"]]

所以,我想你可能会问includeseager_load的不同部分,也就是

为了效率和优化,我们应该使用两个单独的查询还是一个LEFT OUTER JOIN查询?

这也让我感到困惑。经过一番挖掘,我发现了秋田法比奥的文章说服了我。以下是一些参考资料和示例:

在某些情况下,怪物外部连接比许多较小的查询要慢。底线是:一般来说,把怪物分成小的似乎更好。这避免了笛卡尔积过载的问题。 结果集越长越复杂,这一点就越重要,因为Rails需要处理的对象越多。分配和处理数百或数千个复制的小对象从来都不是一件好事。

从Rails查询数据的示例

代码语言:javascript
复制
> Blog.eager_load(:posts).map(&:name).count
  SQL (0.9ms)  SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id"
 => 3

LEFT OUTER JOIN查询返回的SQL数据示例

代码语言:javascript
复制
sqlite>  SELECT "blogs"."id" AS t0_r0, "blogs"."name" AS t0_r1, "blogs"."author" AS t0_r2, "blogs"."created_at" AS t0_r3, "blogs"."updated_at" AS t0_r4, "posts"."id" AS t1_r0, "posts"."title" AS t1_r1, "posts"."created_at" AS t1_r2, "posts"."updated_at" AS t1_r3, "posts"."blog_id" AS t1_r4 FROM "blogs" LEFT OUTER JOIN "posts" ON "posts"."blog_id" = "blogs"."id";
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|1|Post 1-1|2015-11-11 15:22:35.053689|2015-11-11 15:22:35.053689|1
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|2|Post 1-2|2015-11-11 15:22:35.058113|2015-11-11 15:22:35.058113|1
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|3|Post 1-3|2015-11-11 15:22:35.062776|2015-11-11 15:22:35.062776|1
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|4|Post 1-4|2015-11-11 15:22:35.065994|2015-11-11 15:22:35.065994|1
1|Blog 1|someone|2015-11-11 15:22:35.015095|2015-11-11 15:22:35.015095|5|Post 1-5|2015-11-11 15:22:35.069632|2015-11-11 15:22:35.069632|1
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|6|Post 2-1|2015-11-11 15:22:35.078644|2015-11-11 15:22:35.078644|2
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|7|Post 2-2|2015-11-11 15:22:35.081845|2015-11-11 15:22:35.081845|2
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|8|Post 2-3|2015-11-11 15:22:35.084888|2015-11-11 15:22:35.084888|2
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|9|Post 2-4|2015-11-11 15:22:35.087778|2015-11-11 15:22:35.087778|2
2|Blog 2|someone|2015-11-11 15:22:35.072871|2015-11-11 15:22:35.072871|10|Post 2-5|2015-11-11 15:22:35.090781|2015-11-11 15:22:35.090781|2
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|11|Post 3-1|2015-11-11 15:22:35.097479|2015-11-11 15:22:35.097479|3
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|12|Post 3-2|2015-11-11 15:22:35.103512|2015-11-11 15:22:35.103512|3
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|13|Post 3-3|2015-11-11 15:22:35.108775|2015-11-11 15:22:35.108775|3
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|14|Post 3-4|2015-11-11 15:22:35.112654|2015-11-11 15:22:35.112654|3
3|Blog 3|someone|2015-11-11 15:22:35.093902|2015-11-11 15:22:35.093902|15|Post 3-5|2015-11-11 15:22:35.117601|2015-11-11 15:22:35.117601|3

我们从Rails中得到了预期的结果,而从SQL中得到了更大的结果。这就是LEFT OUTER JOIN的效率损失。

所以我的结论是,更喜欢includes而不是eager_load

我在研究期间总结了一篇关于Rails中的加载、包含、引用和连接的博文。希望这能帮上忙。

参考文献

  • 删除Rails应用程序中的N+1查询
  • Rails :包含与:联接
  • 预加载,Eagerload,Includes和Joins
  • 使用Rails 2.1滚动-第一个完整教程-第2部分
票数 4
EN

Stack Overflow用户

发布于 2015-10-08 19:03:07

因此,事实证明,ActiveRecord曾经试图将所有内容都放在一个查询中,但后来选择了这不是一个好主意。

我在上面的查询和4000条记录中研究了这个问题。

快速分析:

eager_load花了2600毫秒。包括花了72毫秒。

eager_load花费了36倍的时间。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33022674

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档