ActiveRecord根据操作顺序进行重新查询

时间:2013-05-31 16:59:22

标签: ruby-on-rails

Ruby和Rails的新手,请原谅我,如果我的术语有点偏。

我正在努力优化一些继承的代码并观察日志我看到查询重复自己由于.rabl文件中的这样的行:

node(:program) { |user| (!user.programs.first.nil?) ? user.programs.first.name : ''  }

userprogram都是有效记录对象

转到rails控制台,我可以复制问题,但我也可以获得预期的行为,这只是一个查询:

>u = User.find(1234)
 User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE [...]
> (!u.programs.first.nil?) ? u.programs.first.name : ''
 Program Load (0.2ms)  SELECT `programs`.* FROM `programs` [...]
 Program Load (0.3ms)  SELECT `programs`.* FROM `programs` [...]
=> "Output"

请注意,在控制台中重复三元语句总是给我2个查询。

我可以得到预期的行为:

> newu = User.find(12345)
  User Load (3.8ms)  SELECT `users`.* FROM `users` WHERE [...]
> newu.programs
  Program Load (0.6ms)  SELECT `programs`.* FROM `programs` [...]
> (!newu.programs.first.nil?) ? newu.programs.first.name : ''
=> "Output"

现在重复三元语句根本不会重新查询。

所以问题是:为什么调用newu.programs会改变行为?不应该调用u.programs.first.nil?也以同样的方式加载所有程序记录吗?

1 个答案:

答案 0 :(得分:1)

通过关联,first不是[0]的糖。

如果加载了关联,那么它只返回数组的第一个元素。如果未加载,则会使数据库查询仅加载该元素。它不能坚持在关联缓存中(至少不是没有更聪明),因此first的下一个查询会再次执行查询(如果打开则将使用查询缓存)

Rails假设的是,如果关联很大,并且你只使用了它的一个元素,那么加载整个事物将是愚蠢的。如果不是这种情况并且您只是使用一个项目,但是您反复使用它,这可能有点烦人。

为了避免这种情况,你可以将项目分配给一个局部变量,这样你就可以真正只调用first一次,或者

newu.programs[0]

将加载整个关联(仅一次)并返回第一个元素。

Rails与include?做同样的事情。它将运行一个查询,测试特定项是否在集合中(除非加载了集合),而不是加载整个集合。