It is known that instantiating ActiveRecord objects is slow. When querying, most of the time it's necessary to instantiate them so we can invoke their methods or navigate their relationships.
When you need to do a custom select with a group by you generally do:
Model.select(...).group(...).all
This will create an ActiveRecord instance for each of the results, with accessors for each of the elements in the select clause. This is very expensive compared to just creating arrays of the values as the result of the query.
So let's do it:
class ActiveRecord::Relation def raw connection.execute(to_sql) end end
You can use it like this:
Model.select('id, sum(hours)').group('id').raw.each do |id, hours| # ... end
Here's a sample IRB session showing the performance gain:
irb(main):024:0> WorkedHour.where(work_group_id: 1).select('worker_id, worked_at, sum(hours)').group('worker_id, worked_at').all.length => 3369 irb(main):025:0> time = Time.now; WorkedHour.where(work_group_id: 1).select('worker_id, worked_at, sum(hours)').group('worker_id, worked_at').all; Time.now - time => 0.314908 irb(main):026:0> time = Time.now; WorkedHour.where(work_group_id: 1).select('worker_id, worked_at, sum(hours)').group('worker_id, worked_at').raw.to_a; Time.now - time => 0.044524 irb(main):027:0> 0.314908 / 0.044524 => 7.072769742161531
So, creating arrays as results for 3369 objects instead of ActiveRecord objects results in a 7x performance boost.