How to avoid N+1 queries in Rails “each” loops?

In a todo list-style app, I have the following ActiveRecord model method:

class Task < ActiveRecord::Base

def project_name
project.tasks.length > 0 ? “#{project.name} - #{name}” : project.name
end
end

``

The idea is to provide additional project information if there are one or more tasks on the project.

However, when invoked regularly on views this creates performance concerns (especially with a growing data set).

What is the best way to optimize this query so that it doesn’t create N+1 query type issues when invoked from “each” loops in the view?

(Feel free to post answers on StackOverflow).

You would probably want to make a counter cache for tasks on the
project (you can find out more here:
),
and additional when retrieving tasks from the database, when you
know you will need their project data, use eager loading (more here:
).
Michał.

+1 what Michał said about eager-loading. One additional tricky thing: prefer size over length for associations and relations. In plain Ruby, size, length and count are more or less identical - but for ActiveRecord collections they have slightly different meanings:

  • length is the most straightforward: it’s ALWAYS the number of records in the collection. If the collection isn’t currently loaded, calling length on it will trigger a SQL query to load all the records.

  • count is the opposite: it ALWAYS runs a SQL query. It doesn’t load records, it uses SQL’s COUNT() function. It can also return things that aren’t numbers; doing Task.group(:project_id).count will give you back a hash with project_ids as keys and the number of matching tasks as values.

  • size is the middle: if the collection is loaded, it works like length. Otherwise it works like count

–Matt Jones

Wow, that’s a great explanation Matt. I was not aware of the subtle differences.

It sounds like a sensible default would be to use size, unless you know you need a specific behaviour…?