Rails eager_load, joins and includes - when to use what

If you're developing a web application with Ruby on Rails, you may encounter slow queries as your database grows. To enhance performance, you can use eager loading, which lets you load related records upfront instead of separate queries for each record.

Eager loading

Eager loading is a method that permits you to retrieve associated records in a single query instead of multiple database queries. This can notably improve your application's performance by minimizing the number of database calls.
For instance, suppose you have a User model with many projects. In that case, you can retrieve all the users and their related projects in one query using eager loading:

# LEFT OUTER JOIN - this means that all records from the primary table are returned, along with any associated records that match the specified conditions.
@users = User.eager_load(:projects).where("projects.published = ?", true)

This code will obtain all the users and their associated projects in a single query instead of a separate query for each user's projects. Apart from eager loading, Rails also provides other loading techniques that can optimize your queries.

Lazy loading

This approach loads associated records when they are needed rather than loading them upfront. This can be helpful when you have a large number of associations and don't want to load them all at once.

@user = User.find(1) 
# Projects are loaded when called
@projects = @user.projects

Preloading

This technique is comparable to eager loading, but instead of joining the related records in a single query, it makes a separate query for each association. This can be useful when you need to modify the association's query or when the related records are not needed in every query.

# loads the association data in a separate query
@users = User.preload(:projects)

Includes vs. Joins

Both includes and joins can be used for eager loading, but they operate differently. Includes will load the associated records using separate queries, while joins will join the associated records into a single query. Both methods are very smart in Rails - they can create left outer join automagically when needed.

# load associations separately from the main query - I don't want to load them all at once
@users = User.includes(:projects)
# I know that I will iterate projects per each user, so please prepare one sql query and give me the all results now
@users = User.joins(:projects)

So:

  • use includes when you don't need the associated records in every query. Because it's smart enough to decide: If no where clause references the included tables, it uses preload(), if we have where then it's switching to eager_load()
  • and use joins when you do.

In conclusion, optimizing database queries is essential in building high-performance web applications. Eager loading, lazy loading, preloading, and other loading techniques can help you achieve this goal. By understanding these methods and when to apply them, you can enhance the performance of your Rails application.