How to use includes on a single column in Rails

时间:2017-07-10 15:26:47

标签: ruby-on-rails

I'm looping through companies and collecting all of the employee names for each company:

companies = Company.all
companies.each do |company|
  employee_names = company.employees.pluck(:name)
  # do work, I still need access to the company object
end

That will do a separate query to get the employee names for each company. The Bullet gem suggested to add includes(:employees). So I attempted:

companies = Company.includes(:employees)
companies.each do |company|
  employee_names = company.employees.collect(&:name)
  # do work
end

This successfully minimizes the amount of queries, but the request actually takes longer! I'm assuming that is because the includes loads the entire set of employees into memory, but I only need the name.

Is there a better way to do an includes but only on a specific column (or columns)?

3 个答案:

答案 0 :(得分:1)

您是否Company已在employee_id上编入索引。在我看来,问题是你现在正在查询你曾经在应用程序内存中做的工作。如果您想根据某些员工查询公司信息(这似乎是这种情况),您可能希望公司表具有employee_id的索引。

答案 1 :(得分:1)

更好的方法是特定于数据库。对于PostgreSQL

SELECT_CLAUSE = <<~SQL
  companies.*,
  array(
    SELECT name FROM employees WHERE company_id = companies.id
  ) as employee_names
SQL
# ...
companies = Company.select(SELECT_CLAUSE)
companies.each do |company|
  # company.employee_names # => Array
end

或者根本不实例化模型实例:

PLUCK_COLUMN = 'array(SELECT name FROM employees WHERE company_id = companies.id)'
# ...
companies = Company.pluck( :id, :title, PLUCK_COLUMN )
# [
#   [ 1, 'Apple', [ 'John', 'Vasya', ... ] ],
#   ...

如果您有大量数据,请不要忘记索引和分页。

答案 2 :(得分:0)

我不知道这个特殊用例的任何内置Rails解决方案。

但是,您可以通过模拟排序 includes所做的事情来解决这个问题,最后调用pluck的不同之处在于:

companies = Company.all.to_a
#=> [#<Company:...>, #<Company:...>, ...]

company_ids = companies.map(&:id)
#=> [1, 2, 3, 5, ...]

company_employee_map = Employee.
  where(company_id: company_ids).
  pluck(:company_id, :name).
  inject({}) { |hash, (company_id, name)|
    hash[company_id] ||= []
    hash[company_id] << name
    hash
  }

companies.each do |company|
  employee_names = company_employee_map[company.id]
end

代码未经测试,因此可能需要进行一些调整。