Benito Serna Tips and tools for Ruby on Rails developers

Joins does not preload

October 24, 2021

When you are working with SQL, to use information from other tables you would use a JOIN…

And if you are just starting to learn ActiveRecord comming from SQL, you could think that by using the joins method you will be able to later use the data from the associations without doing a new query.

And this is true and false at the same time…

It will let you use the association on the query

Yes you will be able to use the data of the associations but just in the same query. If you want to use the data from an association after the query result has been returned, you will need a new query.

To see it more clearly let’s try an example…

This will do an INNER JOIN:

posts = Post.joins(:comments)

And this will trigger a new database call for each post:

posts.map(&:comments).to_a

Because joins does not preload the data.

If you see the produced query you will see that what rails does, is to use the association data to build the join to the comments table.

SELECT posts.*
FROM posts
INNER JOIN comments ON comments.post_id = posts.id

With that join you will be able to use the associated relation (in this case the comments table) in the query, by chaining other methods from the query interface like a where, order, group

For example…

posts = Post
  .joins(:comments)
  .where(comments: { body: "hola" })

This will build the SQL clause with a WHERE like this:

SELECT posts.*
FROM posts
INNER JOIN comments ON comments.post_id = posts.id
WHERE comments.body = 'hola'

You can do the same with other methods like order:

posts = Post
  .joins(:comments)
  .order("comments.created_at DESC")

This will build the SQL clause with an ORDER like:

SELECT posts.*
FROM posts
INNER JOIN comments ON comments.post_id = posts.id
ORDER BY comments.created_at DESC

To preload you will need another method

And if you really want to preload the comments you will need to use preload , includes or eager_load, to actually tell ActiveRecord to also fetch the associated records. For example:

This will do an INNER JOIN and a also preload the comments:

posts = Post
  .joins(:comments)
  .where(comments: { body: "hola" })
  .preload(:comments)

And now this will not trigger a new database call for each post:

posts.map(&:comments).to_a

More about joins and methods to preload

If you want learn more about the difference between joins and the three method that you can use to preload associations preload, includes and eager_load. You can read this post:

What is the difference between includes, preload, eager_load and joins in ActiveRecord?

Related articles

No more… “Why active record is ignoring my includes?”

Get for free the first part of the ebook Fix n+1 queries on Rails that will help you:

  • Explain what is an n+1 queries problem
  • Identify when ActiveRecord will execute a query
  • Solve the latest comment example
  • Detect n+1 queries by watching the logs
  • Learn the tools to detect n+1 queries
Get the first part of the ebook for free