Observe a ordem de: inclui para has_many: até

Eu estava olhando meu código Rails 3.2.13 para reduzir as consultas de banco de dados quando encontrei algo muito estranho.

# user.rb
class User < ActiveRecord::Base
has_many
:attendances
has_many
:events, :through => :attendances
end

Busquei um usuário e queria verificar se havia eventos:

u = User.includes([:attendances, :events]).find(1) # => #<User>
User Load (0.7ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Attendance Load (0.9ms) SELECT "attendances".* FROM "attendances" WHERE "attendances"."user_id" IN (1)
Event Load (0.7ms) SELECT "events".* FROM "events" WHERE "events"."id" IN (14, 15, 16)
u
.events.any? # => true
# (no query happening)
u
.attendances.any? # => true
(1.1ms) SELECT COUNT(*) FROM "attendances" WHERE "attendances"."user_id" = 1

Por que o ActiveRecord fez uma consulta de contagem quando já deveria estar com os atendimentos carregados?

Então, mudei a ordem dos includes:

u = User.includes([:events, :attendances]).find(1) # => #<User>
User Load (0.5ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 1]]
Attendance Load (0.6ms) SELECT "attendances".* FROM "attendances" WHERE "attendances"."user_id" IN (1)
Event Load (0.5ms) SELECT "events".* FROM "events" WHERE "events"."id" IN (14, 15, 16)
Attendance Load (0.4ms) SELECT "attendances".* FROM "attendances" WHERE "attendances"."user_id" IN (1)
u
.events.any? # => true
# (no query happening)
u
.attendances.any? # => true
# (no query happening)

Agora ActiveRecord não fez nenhuma consulta de contagem, mas em vez disso, consultou os atendimentos inicialmente DUAS VEZES? E nem mesmo recorreu a um cache para a mesma consulta?

O que você deveria fazer?

Bem, escolha entre praga ou cólera:

Peste: Se incluir a associação: through (presenças) antes da associação mais distante (eventos), a associação mais próxima NÃO será carregada.

Cólera: Mas se você incluir a associação: through após a associação mais distante, a associação mais próxima será carregada DUAS VEZES.

Você escolhe. (Eu prefiro cólera)