What\'s the difference between \"includes\" and \"joins\" in ActiveRecord query? Can anyone explain to me with the following two associated
:joins
is the ActiveRecord version of JOINS, the SQL request.
Store.joins(:car).to_sql
=> SELECT stores.* FROM stores INNER JOIN cars ON cars.store_id = categories.id
So behind the seen it is an implicit INNER JOIN
. It will thus indeed return all stores that have a car (because of the inner_join
, a left_outer_join
would have had a different behaviour). See more here.
Whereas :includes
is not a query command. It solves the n+1 query issue by eager_loading
the Car model on the Store model. See more one eager_loading
here.
stores = Store.includes(:car)
will thus return all stores, and allows to do stores.first.car
without firing a new query.
Joins will just join the tables and bringsselected fields in return. if you call associations on joins query result, it will fire database queries again
Include will eager load the included associations and add them in memory. Include loads all the included tables attributes. If you call associations on include query result, it wont fire any queries
You can find a detailed explanation with examples in this article of mine: Active Record Associations Tips & Tricks.
:joins returns read-only objects, :includes does not
:joins uses inner join, :includes uses outer join.
the main reason of :includes is eager loading, to avoid the N+1 problem of loading in attributes of each object using a separate query.
:joins
joins tables together in sql, :includes
eager loads associations to avoid the n+1 problem (where one query is executed to retrieve the record and then one per association which is loaded).
I suggest you read their sections in Rails Guides to get more info.
stores = Store.joins(:car)
This will return all stores for which there is a car. stores[0].car
will result in another query.
stores = Store.includes(:car)
This will return all stores, car or no car. stores[0].car
will not result in another query.
stores = Store.includes(:car).joins(:car)
This will return all stores with a car. stores[0].car
will not result in another query. I wouldn't recommend this for has_many
relationships, but it works great for has_one
.
TL; DR
Joins:
a.joins(:b).to_sql
=> "SELECT \"a\".* FROM \"a\" INNER JOIN \"b\" ON \"b\".\"id\" = \"a\".\"b_id\""
Includes:
a.includes(:b).to_sql
=> "SELECT \"a\".* FROM \"a\"
Both:
a.includes(:b).joins(:b).to_sql
=> "SELECT \"a\".\"id\" AS t0_r0, \"a\".\"a_field_1\" AS t0_r1, \"a\".\"a_field_2\" AS t0_r2, \"a\".\"a_field_3\" AS t0_r3, \"b\".\"a_field_1\" AS t1_r1, \"b\".\"a_field_2\" AS t1_r2, \"b\".\"a_field_3\" AS t1_r3 FROM \"a\" INNER JOIN \"b\" ON \"b\".\"id\" = \"a\".\"plan_id\""