Rails provides four different ways to load association data. In this blog we are going to look at each of those.
This is how
Since
With preload where clauses can be applied.
However it is smarter than
As you can see
So
In the above case a single query was done.
This is exactly what
In the above case no posts data is selected. Above query can also
produce duplicate result. To see it let’s create some sample data.
With the above sample data if we execute
We can avoid the duplication by using
Also if we want to make use of attributes from
Note that using
Preload
Preload loads the association data in a separate query.User.preload(:posts).to_a
# =>
SELECT "users".* FROM "users"
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1)
includes
loads data in the default case.Since
preload
always generates two sql we can’t use posts
table in
where condition. Following query will result in an error.User.preload(:posts).where("posts.desc='ruby is awesome'")
# =>
SQLite3::SQLException: no such column: posts.desc:
SELECT "users".* FROM "users" WHERE (posts.desc='ruby is awesome')
User.preload(:posts).where("users.name='Neeraj'")
# =>
SELECT "users".* FROM "users" WHERE (users.name='Neeraj')
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (3)
Includes
Includes loads the association data in a separate query just likepreload
.However it is smarter than
preload
. Above we saw that preload
failed for query User.preload(:posts).where("posts.desc='ruby is awesome'")
. Let’s try same with includes.User.includes(:posts).where('posts.desc = "ruby is awesome"').to_a
# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
"posts"."title" AS t1_r1,
"posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
WHERE (posts.desc = "ruby is awesome")
includes
switches from using two separate queries to creating a single LEFT OUTER JOIN
to get the data. And it also applied the supplied condition.So
includes
changes from two
queries to a single query in some cases. By default for a simple case
it will use two queries. Let’s say that for some reason you want to
force a simple includes
case to use a single query instead of two. Use references
to achieve that.User.includes(:posts).references(:posts).to_a
# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
"posts"."title" AS t1_r1,
"posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
Eager load
eager loading loads all association in a single query usingLEFT OUTER JOIN
.User.eager_load(:posts).to_a
# =>
SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0,
"posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3
FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"
includes
does when it is forced to make a single query when where
or order
clause is using an attribute from posts
table.Joins
Joins brings association data usinginner join
.User.joins(:posts)
# =>
SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
def self.setup
User.delete_all
Post.delete_all
u = User.create name: 'Neeraj'
u.posts.create! title: 'ruby', desc: 'ruby is awesome'
u.posts.create! title: 'rails', desc: 'rails is awesome'
u.posts.create! title: 'JavaScript', desc: 'JavaScript is awesome'
u = User.create name: 'Neil'
u.posts.create! title: 'JavaScript', desc: 'Javascript is awesome'
u = User.create name: 'Trisha'
end
User.joins(:posts)
then this is the result we get#<User id: 9, name: "Neeraj">
#<User id: 9, name: "Neeraj">
#<User id: 9, name: "Neeraj">
#<User id: 10, name: "Neil">
distinct
.User.joins(:posts).select('distinct users.*').to_a
posts
table then we need to select them.records = User.joins(:posts).select('distinct users.*, posts.title as posts_title').to_a
records.each do |user|
puts user.name
puts user.posts_title
end
joins
means if you use user.posts
then another query will be performed.
No comments:
Post a Comment