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, makesincludesbehaves 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