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)?
答案 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
代码未经测试,因此可能需要进行一些调整。