rails中的Preload, Eagerload, Includes and Joins

原文:http://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html(有墙)
rails提供四种不同的方法来加载关联数据。这篇博客中我们来一一介绍他们。
1,Preload
Preload使用单独的查询加载关联的数据。

User.preload(:posts).to_a

# =>
SELECT "users".* FROM "users"
SELECT "posts".* FROM "posts"  WHERE "posts"."user_id" IN (1)

这是includes默认加载数据的方式。
因为preload方式经常会生成两个sql查询语句,所以我们不能对post表进行where条件查询。下边的查询会导致错误。

User.preload(:posts).where("posts.desc='ruby is awesome'")

# =>
SQLite3::SQLException: no such column: posts.desc:
SELECT "users".* FROM "users"  WHERE (posts.desc='ruby is awesome')

使用preload时where可以使用的情况。(where条件只能筛选User表的条件)

User.preload(:posts).where("users.name='Neeraj'")

# =>
SELECT "users".* FROM "users"  WHERE (users.name='Neeraj')
SELECT "posts".* FROM "posts"  WHERE "posts"."user_id" IN (3)

Includes(eager loading)
跟preload类似,includes将关联数据加载到一个单独的查询。
然而他比Preload更聪明。上边我们看到了preload查询“User.preload(:posts).where(“posts.desc=’ruby is awesome’”)”时会失败。让我们用includes试一试。

User.includes(:posts).where('posts.desc = "ruby is awesome"').to_a

# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
       "posts"."title" AS t1_r1,
       "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
WHERE (posts.desc = "ruby is awesome")

我们可以看到includes用两个单独的查询来创建一个单独的“左外链接”来获得数据。并且它还使用了提供的条件。
(注意:由于文章是2013年,目前(rails5.0.1)的写法应该是这样的。)

CheckItem.includes(:check_scores).where(:check_scores => {id:1968})

所以includes有时把两个查询改变到一个单独的查询中。默认情况下,对于一个简单的情景,它将使用两个查询。由于某些原因,在某些情况下你想让includes使用一个查询而不是两个。那就使用references来实现把。

User.includes(:posts).references(:posts).to_a

# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
       "posts"."title" AS t1_r1,
       "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

上边这种情况是只使用一个查询的情况
Eager load
Eager load使用left outer join用一个单独的查询加载所有关联。

User.eager_load(:posts).to_a

# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
       "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"

这正是当includes被要求做一个单独的查询,这个查询中“where”或者“order”子句正在使用posts表的一个属性时,includes所做的。
Joins(lazy loading)
joins使用innner join引入关联数据。

User.joins(:posts)

# =>
SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"

在上边的情景中,没有posts表的数据被选中。上边的查询能够造成重复的结果。为了看到这种情况,我们呢来创造些重复的数据。

def self.setup
  User.delete_all
  Post.delete_all

  u = User.create name: 'Neeraj'
  u.posts.create! title: 'ruby', desc: 'ruby is awesome'
  u.posts.create! title: 'rails', desc: 'rails is awesome'
  u.posts.create! title: 'JavaScript', desc: 'JavaScript is awesome'

  u = User.create name: 'Neil'
  u.posts.create! title: 'JavaScript', desc: 'Javascript is awesome'

  u = User.create name: 'Trisha'
end

上边的这种情况,我们使用User.joins(:posts)将会得到下边的结果。

#<User id: 9, name: "Neeraj">
#<User id: 9, name: "Neeraj">
#<User id: 9, name: "Neeraj">
#<User id: 10, name: "Neil">

我们可以使用distinct来避免重复。

User.joins(:posts).select('distinct users.*').to_a

同样的如果我们使用posts表中的属性,我们需要使用select语句。

records = User.joins(:posts).select('distinct users.*, posts.title as posts_title').to_a
records.each do |user|
  puts user.name
  puts user.posts_title
end

注意,使用joins意味着如果你使用user.posts,另一个查询将会被执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值