Vad*_*dim 5 activerecord ruby-on-rails eager-loading ruby-on-rails-3
我有以下模型:Game 和 Pick。Game 和 Pick 之间存在一对多的关联。还有第三个模型叫做 Player,一个 Player 有很多 Picks。
Player 类中有一个方法可以为给定的游戏找到一个选择,或者如果它不存在则创建一个新的。
class Player < ActiveRecord::Base
has_many :picks
def pick_for_game(game)
game_id = game.instance_of?(Game) ? game.id : game
picks.find_or_initialize_by_game_id(game_id)
end
end
Run Code Online (Sandbox Code Playgroud)
我想急切地为每个选择加载游戏。但是,如果我这样做
picks.find_or_initialize_by_game_id(game_id, :include => :game)
Run Code Online (Sandbox Code Playgroud)
它首先在此查询运行时获取选择(该方法运行多次),然后在访问每个选择时获取游戏。如果我将 default_scope 添加到 Pick 类
class Pick < ActiveRecord::Base
belongs_to :game
belongs_to :player
default_scope :include => :game
end
Run Code Online (Sandbox Code Playgroud)
它仍然为每个选择生成 2 个选择语句,但现在它在选择后立即加载游戏,但它仍然没有像我期望的那样进行连接。
Pick Load (0.2ms) SELECT "picks".* FROM "picks" WHERE "picks"."game_id" = 1 AND ("picks".player_id = 1) LIMIT 1
Game Load (0.4ms) SELECT "games".* FROM "games" WHERE ("games"."id" = 1)
Run Code Online (Sandbox Code Playgroud)
首先,find不支持将includeorjoin作为参数。(正如 mipsy 所说,支持 find 是没有意义的include,因为它的查询数量与稍后加载它的查询数量相同。)
其次,include急切地加载关联,所以像
Person.includes(:company)
Run Code Online (Sandbox Code Playgroud)
大致相当于做:
Person.all.each { |person| Company.find(person.company_id) }
Run Code Online (Sandbox Code Playgroud)
我说大致相当于是因为前者有O(1)(实际上是两个)查询,而后者是O(n)查询,其中n是人数。
然而,联接只是一个查询,但联接的缺点是您不能总是使用检索到的数据来更新模型。要加入,你可以这样做:
Person.join(:companies)
Run Code Online (Sandbox Code Playgroud)
您可以在 Rails Guide 中阅读有关连接表的更多信息。
总而言之,加入并不是急切加载,因为它不是加载关联,而是一次将两条数据加载在一起。我意识到两者之间有一条奇怪的细线,但急切加载是抢先获取其他数据,但您稍后不会通过联接获取该数据,或者您已经在原始查询中获取了它!希望这是有道理的。
| 归档时间: |
|
| 查看次数: |
6018 次 |
| 最近记录: |