Database is considered an expensive resource, and while developing our application we should be particular about the number of DB queries made, in general fewer the DB queries executed, the better the performance.
One way is to use
eager loading which help us to decrease the number of DB queries performed, by preloading the associated content from database.
Lets assume we have two models
one-to-many relationship between them - i.e
Owner has many
An instance variable
@cars is assigned a bunch of cars in the controller and used in views as follows:
DB queries executed in log for the above request.
First query is executed to fetch all the
Cars related data, and then again one DB query is executed each time, whenever
car.owner.name was called in our view, which required the associated
Owner data, making a total of N+1 query where N is the number of cars.
This is a common performance antipattern and it is termed infamously as N+1 query problem.
Eager Loading to Rescue!
In Rails ActiveRecord, we have different approaches to fetch data from an association of models. Rails provides us with four methods, namely
joins() which we can use judiciously to tackle the N+1 query problem.
It makes exact two DB queries where the first query is used to fetch the
Car related data and second query is used to fetch all the associated
Owner data, using the ids from the first query.
We have reduced the number of queries successfully from
4(3 + 1) to
2 query. It may not seem to be drastic improvement in this case, but it make picture more clear if suppose you had
5000 cars listed in your DB, then in case of N+1 query problem,
(5000 + 1) query will be executed instead of
2 query using
The only disadvantage using preload is that we cannot have any data filters on
Owners table. It will throw us an exception, as two separate queries are executed in
preload() and we haven’t joined our
Owner table anywhere.
It makes a single DB query and uses
LEFT OUTER JOIN to club together the two tables.
It has an upper hand over
preload() in the terms when we have to add data filter or order(
ORDER BY clause), on the second table, or
Owner in this case.
Sometimes it may become tough to decide which one to opt for between
eager_load() for better performance. Rails has
includes() for the very same purpose, and does this task for you. It decides on itself which approach to take so that you can sit back and relax.
includes() smartly switches to making a single DB query using
LEFT OUTER JOIN, when ordering or filtering is required on the associated data.
INNER JOIN to fetch the associated data from tables.
It seems like we are encountering again our N+1 query problem, and this happens because by default
joins() does not include the
owners column in result, and you have to specify explicitly all columns required using a
select() clause on
You just have to change
car.owner_name in your view as now
owner_name is an attribute on