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…
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
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
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?
Learn just enough fundamentals to be fluent preloading associations with ActiveRecord, and start helping your team to avoid n+1 queries on production.