I have the following models: Game and Pick. There's a one to many association between Game and Pick. There's a third model called Player, a Player has many Picks.
There's a method in the Player class that finds a pick for a given game or creates a new one if it doesn't exist.
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
I want to eager load the games for each pick. However if I do
pick开发者_Python百科s.find_or_initialize_by_game_id(game_id, :include => :game)
It first fetches the picks when this query is run (the method is run multiple times), then fetches the games as each pick is accessed. If I add a default_scope to the Pick class
class Pick < ActiveRecord::Base
belongs_to :game
belongs_to :player
default_scope :include => :game
end
It still generates 2 select statements for each pick, but now it loads the game right after the pick, but it still doesn't do a join like I'm expecting.
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)
First, find
doesn't support having include
or join
as a parameter. (As mipsy said, it doesn't make sense for find to support include
as it would be the same number of queries as loading it later.)
Second, include
eagerly loads the association, so something like
Person.includes(:company)
is roughly equivalent to doing:
Person.all.each { |person| Company.find(person.company_id) }
I say roughly equivalent to because the former has O(1)
(really two) queries whereas the latter is O(n)
queries, where n
is the number of people.
A join, however, would be just one query, but the downside of a join is you can't always use the retrieved data to update the model. To do a join you would do:
Person.join(:companies)
You can read more on joining tables in the Rails Guide.
To sum up, joining isn't eagerly loading because it's not loading the association, it's loading both pieces of data together at once. I realize there's a weird fine line between the two, but eagerly loading is getting other data preemptively, but you wouldn't be getting that data later via a join, or you'd have already gotten it in your original query! Hope that makes sense.
This is the way it's meant to work, I think. Eager loading is primarily used to make iterations over large collections of models more efficient by fetching them all at once-- it won't make any difference if you're just dealing with a single object.
精彩评论