Why does using a joins and includes with find_each result in an incorrect iterating count

Viewed 289

I have a relation like this

users = User.joins(:occupation).includes(:occupation)
count = 0
users.find_each do |u|
  count += 1
end
count

count doesn't return the same number as users.count

It seems to always return a number less than what I anticipate. If I run this code I get the correct count

count = 0
users.each do |u|
  count += 1
end
count

This returns the correct count

I was able to isolate the problem to my user to occupation relationship. However I am not sure what the underlying issue is. Any ideas what could cause this?

1 Answers

You need to consider what's happening in the database to get your answer.

By doing users.joins(:occupation) you are asking for users joined to occupation. This will be an inner join so you'll only get results for a user that has an occupation. In essence what you are asking for is a count of all users who have an occupation.

You could do a users.left_outer_joins(:occupation) which will do an outer join and will return all users (joining them to occupation if they have an occupation).

If you just want to load the users and their associated occupation fairly efficiently you can use users.includes(:occupation) which avoids explicitly joining them together in a single query (unless you reference occupation fields in the query but that's a different question altogether I suspect).

Note that you can get a similar over counting issue if you join to an association that has many values. In that case you'd get multiple rows in the results for each joined record (e.g. a user with two occupations would be counted twice).

Related