preload, eager_load, includes, references, and joins in Rails
There is always a confusion about these query methods. And after some digging, I’ve made my conclusion here: includes
is the outstanding one.
Here comes the demonstation.
Preparation
Environment
- Ruby: 2.2.2
- Rails: 4.2.2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
preload
Always firing two separate queries.
1 2 3 |
|
eager_load
- One query, LEFT OUTER JOINed in any query rather than loaded separately.
- JOIN first, then query by where clause. So you can query on referenced table, without an iteration of
Enumerable#select
. - Works just the same as
includes
+references
.
1 2 3 4 5 6 7 8 |
|
includes
Behaves based on situations, intelligent!
Situation 1, just like preload
1 2 3 4 5 6 7 |
|
Situation 2, just like eager_load
, fired by querying referenced table
1 2 |
|
includes
or eager_load
Consider this snippet:
1 2 3 4 5 6 |
|
Both expressions return the same result, so should we prefer two seperated queries by includes
(also preload
) or the LEFT OUTER JOINed query by eager_load
?
There is a blog post by Fabio Akita talks about the change of Rails 2.1 (see the section entitled “Optimized Eager Loading”). Here are some references:
For some situations, the monster outer join becomes slower than many smaller queries. The bottom line is: generally it seems better to split a monster join into smaller ones, as you’ve seen in the above example. This avoid the cartesian product overload problem.
Example for SQL data returned from LEFT OUTER JOIN query
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
The longer and more complex the result set, the more this matters because the more objects Rails would have to deal with. Allocating and deallocating several hundreds or thousands of small duplicated objects is never a good deal.
As includes
can behave the same as eager_load
in one case, but better in the other case. My conclusion is, prefer includes
over eager_load
.
references
- Works only with
includes
, makesincludes
behaves likeeager_load
1 2 |
|
joins
INNER JOIN, compared to eager_load
(LEFT OUTER JOIN).
1 2 |
|
compared to eager_load
Query by joins
just returns the raw data, whereas the data from eager_load
is filtered by Rails.
1 2 3 4 5 6 |
|
So you need to take caution about iteration on joins
query.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Reference