Published Jun 07, 2022
[
 
]
Eager loadings the mechanism for loading the associated records of the objects
returned by Model.find
using as few queries as possible.
Consider the following code, which finds 10 books and prints their authors’ last_name:
books = Book.limit(10)
books.each do |book|
puts book.author.last_name
end
This code looks fine at the first sight. But the problem lies within the total number of queries executed. The above code executes 1 (to find 10 books) + 10 (one per each book to load the author) = 11 queries in total.
Active Record lets you specify in advance all the associations that are going to be loaded.
With includes
, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Revisiting the above case using the includes
method, we could rewrite Book.limit(10)
to eager load authors:
books = Book.includes(:author).limit(10)
books.each do |book|
puts book.author.last_name
end
The above code will execute just 2 queries, as opposed to the 11 queries from the original case:
SELECT `books`.* FROM `books` LIMIT 10
SELECT `authors`.* FROM `authors`
WHERE `authors`.`book_id` IN (1,2,3,4,5,6,7,8,9,10)
Active Record lets you eager load any number of associations with a single
Model.find
call by using an array, hash, or a nested hash of array/hash with
the includes
method.
Customer.includes(:orders, :reviews)
This loads all the customers and the associated orders and reviews for each.
Customer.includes(orders: {books: [:supplier, :author]}).find(1)
This will find the customer with id 1 and eager load all of the associated orders for it, the books for all of the orders, and the author and supplier for each of the books.
Even though Active Record lets you specify conditions on the eager loaded
associations just like joins
, the recommended way is to use joins instead.
However if you must do this, you may use where
as you would normally.
Author.includes(:books).where(books: { out_of_print: true })
This would generate a query which contains a LEFT OUTER JOIN
whereas the joins
method would generate one using the INNER JOIN
function instead.
SELECT authors.id AS t0_r0, ... books.updated_at AS t1_r5 FROM authors LEFT OUTER JOIN "books" ON "books"."author_id" = "authors"."id" WHERE (books.out_of_print = 1)
books = Book.preload(:author).limit(10)
books.each do |book|
puts book.author.last_name
end
The above code will execute just 2 queries, as opposed to the 11 queries from the original case:
SELECT `books`.* FROM `books` LIMIT 10
SELECT `authors`.* FROM `authors`
WHERE `authors`.`book_id` IN (1,2,3,4,5,6,7,8,9,10)
The preload
method uses an array, hash, or a nested hash of array/hash in the
same way as the includes
method to load any number of associations with a
single Model.find
call. However, unlike the includes
method, it is not
possible to specify conditions for preloaded associations.
With eager_load
, Active Record loads all specified associations using a LEFT OUTER JOIN
.
books = Book.eager_load(:author).limit(10)
books.each do |book|
puts book.author.last_name
end
The above code will execute just 2 queries, as opposed to the 11 queries from the original case:
SELECT DISTINCT `books`.`id` FROM `books` LEFT OUTER JOIN `authors` ON `authors`.`book_id` = `books`.`id` LIMIT 10
SELECT `books`.`id` AS t0_r0, `books`.`last_name` AS t0_r1, ...
FROM `books` LEFT OUTER JOIN `authors` ON `authors`.`book_id` = `books`.`id`
WHERE `books`.`id` IN (1,2,3,4,5,6,7,8,9,10)
The eager_load
method uses an array, hash, or a nested hash of array/hash in
the same way as the includes
method to load any number of associations with a
single Model.find
call. Also, like the includes
method, you can specify
conditions for eager loaded associations.