# Activerecord include queries - performance

**URL:** <https://www.ruby-forum.com/t/activerecord-include-queries-performance/124930>\
**Category:** Rails\
**Created:** [January 3, 2008, 8:51pm UTC](https://www.ruby-forum.com/t/activerecord-include-queries-performance/124930 "2008-01-03T20:51:00Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [January 3, 2008, 8:51pm UTC](https://www.ruby-forum.com/t/activerecord-include-queries-performance/124930/1 "2008-01-03T20:51:00Z")

</div>

Hi,

I noticed ActiveRecord uses some join queries to perform :include  
stuff.  
While this certainly fixes the N+1 problem, I’m wondering about the  
amount of sparse data this generates.

Example:  
Task has many TodoItems  
Both Task and TodoItem only have a name to keep things simple.

Now, when I do :  
Task.find(:all, :include =\> :todo\_items)

it executes:  
SELECT tasks.“id” AS t0\_r0, tasks.“name” AS t0\_r1, todo\_items.“id” AS  
t1\_r0, todo\_items.“name” AS t1\_r1, todo\_items.“task\_id” AS t1\_r2 FROM  
tasks LEFT OUTER JOIN todo\_items ON todo\_items.task\_id = tasks.id

which will transfer back:  
1|task 1|1|item 1|1  
1|task 1|2|item 2|1  
1|task 1|3|item 4|1  
1|task 1|4|item 3|1  
2|task 2|5|item 1|2  
2|task 2|6|item 2|2  
2|task 2|7|item 3|2  
2|task 2|8|item 4|2  
3|task 3|9|item 1|3  
3|task 3|10|item 2|3  
3|task 3|11|item 3|3  
3|task 3|12|item 4|3

As you can see, all fields for tasks are repeated for every todo item.  
While in this small example this wouldn’t matter at all, my guess is  
that it _will_ matter more when there are more fields to a task, more  
items, or deeper nested joins. If I want to join data from say, 5  
tables, all task data will get repeated a lot of times. While using  
mysql through a socket interface, I think it’s neglectable (since  
queries that large will be quite slow anyway), but when connection to  
an external sql server, all this sparse data has to get transferred  
over the network.

Of course it’s still a lot faster than not using the include, since  
looping over tasks would then generate:  
SELECT \* FROM tasks  
SELECT \* FROM todo\_items WHERE (todo\_items.task\_id = 1)  
SELECT \* FROM todo\_items WHERE (todo\_items.task\_id = 2)  
SELECT \* FROM todo\_items WHERE (todo\_items.task\_id = 3)

(n+1) queries which is worse.

But how about this:  
tasks=Task.find(:all)  
todos = TodoItem.find(:all, :conditions =\> { :task\_id =\> tasks.map {|  
x| x.id} })

what will generate:  
SELECT \* FROM tasks  
SELECT \* FROM todo\_items WHERE (todo\_items.“task\_id” IN (1,2,3))

which is 1 query per table.  
of course after this you’d have to find a way to ‘link’ the retrieved  
todos to the corresponding tasks, but I guess activerecord would be  
able to handle this (since it does it for the current, left-outer-join  
behaviour as well).

Now, I’m no database expert so I might be overlooking something.  
Also I haven’t really done any benchmarks.  
But like I said, it looks to me as if the current ActiveRecord  
behaviour is quite sparse for transfering/parsing the data as soon as  
you 3 or 4 tables, with long rows, and many “children”.  
Am I overlooking something?

Any thoughts on this matter would be nice.

Thanks,  
Mathijs

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [January 3, 2008, 9:01pm UTC](https://www.ruby-forum.com/t/activerecord-include-queries-performance/124930/2 "2008-01-03T21:01:21Z")

</div>

On 3 Jan 2008, at 19:50, removed\_email\_address@domain.invalid wrote:

> Hi,
> 
> I noticed ActiveRecord uses some join queries to perform :include  
> stuff.  
> While this certainly fixes the N+1 problem, I’m wondering about the  
> amount of sparse data this generates.

It’s not ideal at all (and compounded by some inefficiencies in the  
way the data that is returned is parsed). There is a ticket/plan to  
reimplement this as separate queries.

Fred

---

<div class="post-metadata">

**Author:** ![casper\_the\_ghost](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/58956e/32.png) [@casper\_the\_ghost](https://www.ruby-forum.com/u/casper_the_ghost)\
**Post date:** [January 4, 2008, 12:56am UTC](https://www.ruby-forum.com/t/activerecord-include-queries-performance/124930/3 "2008-01-04T00:56:26Z")

</div>

On Thu, Jan 03, 2008 at 11:50:46AM -0800, removed\_email\_address@domain.invalid wrote:

> I noticed ActiveRecord uses some join queries to perform :include  
> stuff.  
> While this certainly fixes the N+1 problem, I’m wondering about the  
> amount of sparse data this generates.  
> […]  
> Now, I’m no database expert so I might be overlooking something.  
> Also I haven’t really done any benchmarks.  
> But like I said, it looks to me as if the current ActiveRecord  
> behaviour is quite sparse for transfering/parsing the data as soon as  
> you 3 or 4 tables, with long rows, and many “children”.  
> Am I overlooking something?
> 
> Any thoughts on this matter would be nice.

You are unconsciously assuming that the cost of sending extra data is  
usually greater than the cost of associating records outside the  
database,  
not to mention multiple database optimize and execute cycles. A properly  
indexed database is going to perform the association of records very  
efficiently for you, since that’s one of the things for which RDBMS  
software is designed and optimized. Note that YMMV with poorly written  
RDBMSs; I’ve had MySQL’s query optimizer (not the execution engine, the  
query optimizer) completely hang when trying to deal with joins across a  
lot of tables (18, in this case).

The real cost of sending lots of data is, of course, more than just  
bandwidth. There is the cost of manifesting the result rows in the RDBMS  
and serializing them, which can cause thrashing if the result set is  
really  
big and have an even greater cost. There is the parsing cost for the  
database  
driver to bring the received data into memory. There is the cost of  
hydrating  
the models from the result set in memory.

These costs must be balanced against the smaller costs of the same thing  
done multiple times, including query overhead, to produce the multiple  
result sets, then performing the record association in memory.  
Incidentally, if you have the right indices for a WHERE IN clause to be  
efficient, you have the right indices for the joins to be efficient, so  
that’s a wash.

The real question is where the line is such that the cost of dealing  
with  
the number of rows, M, produced by a multi-table join is greater than  
the  
cost of multiple queries and associating records in code. I think you  
need  
a whole lot of superfluous rows to win with multiple queries, but I  
could  
be wrong. This is where benchmarking comes in. Please do investigate.

> Thanks,  
> Mathijs  
> –Greg
