Как можно избежать запуска единичных выражений для массивов в activerecord и rails 3? - PullRequest
0 голосов
/ 14 декабря 2011

Прошу прощения, если задаю вопрос плохо. У меня есть приложение 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 - это расчет. Часы_минуты действительны для каждой записи расписания в результирующем наборе данных, а затем total_time должен суммировать эти значения и возвращать сумму.

Метод total_time не работает, поскольку employee.merged_children возвращает массив, а табели рабочего времени предназначены для работы с одним объектом Employee.

Есть ли способ структурировать "total_time" так, чтобы он по-прежнему отправлял один запрос к БД? Кажется нелегким перебирать массив merged_children, выполняя запрос для каждого. Не уверен, что прямой вызов за столом Арела поможет или повредит, но я открыт для идей.

Если мы все сделаем правильно, результирующий 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 голосов
/ 21 декабря 2011

Здесь проще всего добавить

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

Для вашей модели сотрудника,

Затем (при условии, что in_range на самом деле является областью действия или методом класса, который выполняет поиск)

children_timesheets.in_range(...)

Должна быть коллекция расписаний, которая вас интересует, и вы можете сделать что-то вроде

children_timesheets.in_range(...).collect(&:hours_minutes).sum
0 голосов
/ 14 декабря 2011

Не проверено с фактическими данными.

  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" для меня.Надеюсь, он вернет вам что-то более приятное

...