在我的Rails应用程序中,我有一个引用其他几个对象的TimeEntry
模型。这是迁移:
class CreateTimeEntries < ActiveRecord::Migration[5.0]
def change
create_table :time_entries do |t|
t.references :user, foreign_key: true
t.references :customer, foreign_key: true
t.references :site, foreign_key: true
t.date :work_date
t.integer :hours
t.integer :minutes
t.references :service, foreign_key: true
t.references :task, foreign_key: true
t.timestamps
end
end
end
我已经使用以下代码汇总了需要User
,Site
和Task
关联字段的报告:
@time_entries = TimeEntry.joins(:customer, :site)
.where('time_entries.work_date >= ? AND time_entries.work_date <= ?', @start_date, @end_date)
.where(customer: @customer)
注意:@customer
已加载到before_action
中,可以是单个客户,也可以是所有300多个客户
为了在我的视图中进行演示,我然后按名称将客户分组到控制器中:
@time_entry_customers = @time_entries.group_by { |time| time.customer.name }
表现如下:
Completed 200 OK in 12308ms (Views: 8508.7ms | ActiveRecord: 924.5ms)
因此,查询并不快速,但VIEW是真正的问题。我在视图中对客户的网站进行了更多分组:
<% @time_entry_customers.each do |customer, time| %>
<tr class="success">
<td colspan="6">
<strong><i class="fa fa-user"></i> <%= customer %></strong>
</td>
</tr>
<% time.group_by { |t| t.site.url }.each do |site, time_entries| %>
<tr>
<td></td>
<td class="site-row" colspan="5"><i class="fa fa-globe"></i> <%= site %></td>
</tr>
<% time_entries.each do |time_entry| %>
<tr>
<td colspan="2"></td>
<td><%= time_entry.work_date %></td>
<td><%= time_entry.try(:user).try(:full_name) %></td>
<td><%= time_entry.task.name %></td>
<td><%= time_entry.hours %> : <%= time_entry.minutes %></td>
</tr>
<% end %>
<tr>
<td></td>
<td class="info" colspan="4"><i class="fa fa-clock-o"></i> Total Time for <%= site %></td>
<td class="info"><%= sum_time_entries_as_hours_and_minutes(time_entries) %></td>
</tr>
<% end %>
<% end %>
在控制器和视图之间,最终会有成千上万的查询(大约有47000 TimeEntries
和~400 Customers
)
我如何重构这一性能?
除users.email
外,所有数据库表都没有任何索引。
答案 0 :(得分:0)
根据此页面的用例和性能预期,有几种方法可以解决此问题。
选项1 :由于您提到页面中显示了47k行,因此用户可能不需要一次查看所有数据。因此,您可以首先显示高级摘要(每个客户和时间在网站上花费的总时间),然后为每个站点和客户行提供选项以进一步展开以显示各个时间条目。要以最有效的方式执行此操作,您可以使用group
中的ActiveRecord
方法和pluck
customer_id,customer_name,site_id,site_name,花费的总时间,在数据库级别进行分组。 / p>
http://guides.rubyonrails.org/active_record_querying.html#group http://guides.rubyonrails.org/active_record_querying.html#pluck
选项2 :考虑到一次显示所有47k记录是必须的,最好只查询显示的列而不是加载整个对象层次结构。与加载所有对象相比,这将表现得更好。下面的代码没有经过测试,但它给你一个想法。
TimeEntry.joins(:customer, :site, :user, :task)..where('time_entries.work_date >= ? AND time_entries.work_date <= ?', @start_date, @end_date).pluck('time_entries.work_date,users.full_name,time_entries.hours,time_entries.minutes,tasks.name, customers.id, customers.name,sites.id, sites.url').order('customers.id,sites.id')
上面的查询将返回所有47k行的数组。您可以在customers.id和sites.id上执行group_by,或者因为它们已经按客户和站点排序,您可以循环遍历所有行,每当您遇到客户或站点与上一行相比发生更改时,您可以显示这些行头。