Find_by_ always uses sql?

Hi,

If I eagerly load a collection during the initial find, do scoped
find_by_ calls later always use sql?

What is the best pattern to use to not do another db trip?
Is there some specialized rails method to handle this case?

eg
Author
has_many :books

larry = Author.find_by_name(‘larry’, :include => :books)

ruby = larry.books.find_by_title(‘Ruby’) # does this ALWAYS do a db
call?

Is the following the best way to get the one (or nil) book?

temp = larry.books.select{|book| book.title == ‘ruby’}
ruby = temp.size == 1 ? temp[0] : nil

Thank you for your comments and time,

Larry

Larry K. wrote:

If I eagerly load a collection during the initial find, do scoped
find_by_ calls later always use sql?

Yes.

call?

Is the following the best way to get the one (or nil) book?

temp = larry.books.select{|book| book.title == ‘ruby’}
ruby = temp.size == 1 ? temp[0] : nil

Yes. Or ruby = larry.books.detect {|book| book.title == ‘ruby’}


We develop, watch us RoR, in numbers too big to ignore.

Larry K. wrote:

Hi,

If I eagerly load a collection during the initial find, do scoped
find_by_ calls later always use sql?

What is the best pattern to use to not do another db trip?
Is there some specialized rails method to handle this case?

eg
Author
has_many :books

larry = Author.find_by_name(‘larry’, :include => :books)

ruby = larry.books.find_by_title(‘Ruby’) # does this ALWAYS do a db
call?

Is the following the best way to get the one (or nil) book?

temp = larry.books.select{|book| book.title == ‘ruby’}
ruby = temp.size == 1 ? temp[0] : nil

Thank you for your comments and time,

Larry

Sense you now have all Larry’s books in memory it is probably better to
search through the memory using ruby calls like:

ruby = larry.books.detect {|book| book.title == ‘ruby’}

On the other hand ruby is a comparatively slow next most database
engines. If you have a large number of books by Larry (or authors in
general) AND you know you only need to find a small finite set of them
(like one) The database may be faster then the Ruby script interrupter
at doing what it does best – finding a subset of items in a table.
Eager loading is best used when you know you will need all or most of
the related information in another table, (eg All the books Larry wrote)
or information in a related table for every member of the first. (eg,
the best selling book for each Author)

Hi Mark,

Thanks for your time, much appreciated.

Yes. Or ruby = larry.books.detect {|book| book.title == ‘ruby’}

Regards,

Larry

Hi –

On Mon, 20 Aug 2007, John M. wrote:

temp = larry.books.select{|book| book.title == ‘ruby’}
ruby = temp.size == 1 ? temp[0] : nil

Thank you for your comments and time,

Larry

Sense you now have all Larry’s books in memory it is probably better to
search through the memory using ruby calls like:

ruby = larry.books.detect {|book| book.title == ‘ruby’}

find and find_by_* on a collection are smart, though: they get rolled
into one database query with the collection query itself. For
example:

$ ./script/console
Loading development environment.

f = Food.find(:first)
=> #<Food:0x3544f04 @attributes={“name”=>“bread”, “id”=>“1”,
“food_group_id”=>“2”}>
f.ingredients.find_by_description(“flour”)
=> #<Ingredient:0x3523c28 @attributes={“id”=>“3”,
“description”=>“flour”, “food_id”=>“1”}>

Here’s the query log:

Food Load (0.000726) SELECT * FROM foods LIMIT 1
Ingredient Load (0.000394) SELECT * FROM ingredients WHERE
(ingredients.food_id = 1) AND (ingredients.“description” = ‘flour’)
LIMIT 1

Notice how the second select combines two where clauses, so the whole
collection isn’t loaded into memory.

The find_by_* methods have some overhead, though, since they have to
pass through method_missing. You can also do:

f.ingredients.find(:first, :conditions => “description = ‘flour’”)

which won’t have that overhead. Of course, find_by_* is nice and
concise, so it’s OK for cases where you’re not looking to tweak
performance.

David