我使用AR includes
方法在对象用户和构建之间执行LEFT OUTER JOIN,其中用户可能会也可能不会有一个建筑协会:
users = User.includes(:building).references(:buildings)
由于我使用references
,因此任何关联的建筑物对象都会被急切加载。
我的期望是,我可以遍历用户列表,并检查用户是否有与之关联的建筑物而不会触发其他查询,但我发现实际上每当我尝试访问建筑物属性时如果没有一个用户,AR会进行另一次SQL调用以尝试检索该建筑物(尽管在后续尝试中它只会返回nil)。
这些查询显然是多余的,因为在初始加入期间会加载关联,并且似乎打败了包含/引用的热切加载的整个目的,因为现在我查看了N次查询的数量等于空关联的数量。
users.each do | user |
# This will trigger a new query when building is not present:
# SELECT "buildings".* FROM "buildings" WHERE "buildings"."address" = $1 LIMIT 1 [["address", "123 my street"]]
if user.building
puts 'User has building'
else
puts 'User has no building'
end
end
用户类:
class User < ActiveRecord::Base
belongs_to :building, foreign_key: 'residence_id'
end
有没有办法检查用户的存在&#39;建立关联而不会触发额外的查询?
ON RAILS 4.2.0 / POSTGRES
更新
感谢@BoraMa汇总了这个test。看起来我们在最近的Rails版本中获得了不同的行为:
OUTPUT(RAILS 4.2.0):
User 1 has building
User 2 has building
User 3 has no building
D, [2016-05-26T11:48:38.147316 #11910] DEBUG -- : Building Load (0.2ms) SELECT "buildings".* FROM "buildings" WHERE "buildings"."id" = $1 LIMIT 1 [["id", 123]]
User 4 has no building
OUTPUT(RAILS 4.2.6)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
OUTPUT(RAILS 5.0.0)
User 1 has building
User 2 has building
User 3 has no building
User 4 has no building
带走:
答案 0 :(得分:3)
听起来好像你在Active Record中得到了bug,这已经在rails 4.2.3中修复了。
在列为零的情况下,Active Record已经知道它甚至不需要尝试加载关联的对象。剩下的案例是受此漏洞影响的案例
答案 1 :(得分:0)
有点像拼写错误,请注意building
而不是buildings
:User.includes(:building).references(:buildings)
这应该触发对每个关联和表使用AS tX_rY
格式的大查询。
答案 2 :(得分:0)
似乎由于rails 4.1存在潜在的冲突,如何隐含#includes应该是多少,请参阅以下open issue。
这段代码的语法都未经过测试,但我会尝试两种方法:
1 /急切加载隐含
users = User.eager_load(:building).preload(:buildings)
2 /分离出两种类型的用户,即建筑物附属的用户,这意味着你甚至不会尝试预先加载建筑物,从而消除了无效率。
users = User.includes(:building).where.not(residence_id: nil).references(:buildings)
users.each do | user|
puts "User has building: #{user} #{user.building}"
end
# No additional references needed to be eager-loaded.
users = User.where(residence_id: nil)
users.each do | user |
puts "#{User} has no building."
end