Rails Model 中的 :through 和 :include 有啥区别?

ruohanc · 2012年04月20日 · 最后由 cactis 回复于 2012年04月21日 · 3735 次阅读

A Guide to Active Record Associations 中描述了 :include:through. 功能看起来是相同的,下面就是两段分别使用 :include:throughmodel 代码,想知道下这两段在功能上有差别吗?


:include

class Supplier < ActiveRecord::Base
  has_one :account, :include => :representative
end

class Account < ActiveRecord::Base
  belongs_to :supplier
  belongs_to :representative
end

class Representative < ActiveRecord::Base
  has_many :accounts
end


:through

class Supplier < ActiveRecord::Base
  has_one :account
  has_one :representative, :through => :account
end

class Account < ActiveRecord::Base
  belongs_to :supplier
  belongs_to :representative
end

class Representative < ActiveRecord::Base
  has_many :accounts
end

跟 find 的 include 参数类似 如果经常要用 account.representative 的话就可以加上 include,这样在查找 account 的时候就会把 representative 查出来并缓存 这样就可以减少数据查询的次数

我是这么理解的

你比较一下输出的 SQL,应该是 inner join 和 left outer join 的区别,文档中也有写

记忆当中是有 inner join 和 left join 的区别 但是试了下,没发现有 inner join 和 left join 的区别 谁来解释下

through 是多对多时的 joint model 比如你上面的例子,Supplier > Account > Representative through Account, 这样 supplier 可以直接找到 rep.

include 如 #1 楼 @tumayun 所说,可以用来避免 n+1 sql query.

@tumayun @nouse @hisea 实验了下

对于 :through

s1 = Supplier.create
r1 = Representative.create
Account.create( :supplier_id => s1.id, :representative_id => r1.id)
Supplier.first.representative

>  Supplier Load (0.3ms)  SELECT "suppliers".* FROM "suppliers" LIMIT 1
>  Representative Load (0.2ms)  SELECT "representatives".* FROM "representatives" INNER JOIN "accounts" ON "representatives"."id" = "accounts"."representative_id" WHERE "accounts"."supplier_id" = 1 LIMIT 1
>  => #<Representative id: 1, name: "rep1", created_at: "2012-04-21 00:12:53", updated_at: "2012-04-21 00:12:53">

对于 :include

似乎这时候 Supplier 根本就不存在 representative 方法,必须绕圈来读取

Supplier.first.account.representative

>  Supplier Load (0.2ms)  SELECT "suppliers".* FROM "suppliers" LIMIT 1
>  Account Load (0.2ms)  SELECT "accounts".* FROM "accounts" WHERE "accounts"."supplier_id" = 1 LIMIT 1
>  Representative Load (0.1ms)  SELECT "representatives".* FROM "representatives" WHERE "representatives"."id" IN (1)
> => #<Representative id: 1, name: "rep1", created_at: "2012-04-21 00:12:53", updated_at: "2012-04-21 00:12:53"> 

model 就是按照主贴内容写的,最后的 SQL 没有各位所说的 join 啊什么的..咋回事呢

:includes 屬於查詢效能優化的問題 執行:

A.limit(10).map{ |a| a.b}


這時候會產生 11 (1 + 10) 次查詢,第一次是 10 筆 A 的查詢,後面逐一對 a 的 b 又要個別查詢,這會產生 10 次 b 的查詢

若加入 :includes

A.includes(:b).limit(10).map{ |a| a.b}


則只會有兩次 sql 查詢,一次是針對 A 本身 (取出 10 筆),另一次會直接以 10 筆 a 的 id,用 in(a1.id, a2.id....) 作為條件直接取得 10 筆 b,這樣只有兩次查詢。由 11 次減為 2 次。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号