Ary Borenszweig

Ary Borenszweig

Avoiding ActiveRecord instance creation to boost performance

2 min
Oct 5 2012
performance
2 min
Oct 5 2012

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.