2017-05-25 55 views
0

在我的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 

我已经把需要从UserSiteTask关联领域的报告,使用此代码:

@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)

因此,查询的不是速度极快,但视图是真正的问题。我做更多的分组视图,通过客户的网站:

<% @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

根据本页的用例和性能预期,有几种解决此问题的方法。

选项1:由于您提到有47k行显示在页面中,用户可能不需要一次查看所有数据。因此,您可以首先显示高级别摘要(每个客户和时间在网站上花费的总时间),然后为每个网站和客户行提供选项以进一步扩展以显示各个时间条目。要以最有效的方式执行此操作,可以使用group方法ActiveRecordpluck中的customer_id,customer_name,site_id,site_name,花费的总时间在DB级别执行分组。

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,也可以通过客户和站点进行排序,您可以循环遍历所有行,并且每当遇到与前一行相比客户或站点发生更改时,都可以显示这些行头。