如何避免在activerecord和rails 3中针对数组运行单数表达式?

时间:2011-12-14 01:32:26

标签: activerecord arel rails-3.1

如果我问这个问题不好,我很抱歉。我有一个Rails 3.1应用程序与模型(简化),如:

class Employee < ActiveRecord::Base
  has_many    :merged_children, :class_name => 'Employee', :foreign_key => "merge_parent_id"
  has_many    :timesheets

  def total_time 
    merged_children.timesheets.in_range(range).hours_minutes.sum 
  end
end

class Timesheet < ActiveRecord::Base
  belongs_to :employee

  def in_range(range)
    # filter records based on transaction_date in range 
  end

  def hours_minutes
    (hours + minutes/60.0).to_f
  end
end

注意:in_range方法本质上充当范围,而hours_minutes是计算。 hours_minutes对结果数据集中的每个时间表记录有效,然后total_time应对这些值求和并返回金额。

“total_time”方法不起作用,因为employee.merged_children返回一个数组,而时间表是针对单个Employee对象运行的。

有没有办法构建“total_time”,以便它仍然向db发送一个查询?迭代merged_children数组,为每个数组发出一个查询似乎是不优雅的。不确定直接调用Arel表是否有帮助或受到伤害,但我对这些想法持开放态度。

如果我们做对了,生成的SQL应该看起来像:

SELECT sum(hours + minutes/60.0)
FROM employees e1 join employees e2 on e1.id = e2.merge_parent_id join timesheets t on t.employee_id = e2.id
WHERE e1.id = [@employee.id] and t.transaction_date BETWEEN [@range.begin] and [@range.end]

非常感谢!

2 个答案:

答案 0 :(得分:0)

未经测试的实际数据。

  range = ((1.day.ago)...(2.days.ago))
  merge_parent = Employee.find(some_id)

  Timesheet.where(:transaction_date => range)
     .joins(:employee).where(:employees => {:merge_parent_id => merge_parent.id})
     .sum('hours*60 + minutes')

   (0.3ms)  SELECT SUM(hours*60 + minutes) AS sum_id FROM "timesheets" INNER JOIN "employees" ON "employees"."id" = "timesheets"."employee_id" WHERE "employees"."merge_parent_id" = 1 AND ("timesheets"."created_at" >= '2011-12-13 03:04:35.085416' AND "timesheets"."created_at" < '2011-12-12 03:04:

为我返回“0”。所以希望它会为你带来更好的东西

答案 1 :(得分:0)

最简单的方法可能是添加

has_many :children_timesheets, :through => :merged_children, :source => :timesheets

到您的员工模型,

然后(假设in_range实际上是一个范围,或者一个查找的类方法)

children_timesheets.in_range(...)

应该是您感兴趣的时间表集合,您可以执行类似

的操作
children_timesheets.in_range(...).collect(&:hours_minutes).sum