ActiveRecords select(:id).collect vs. pluck(:id) methods: Why is pure AR pluck slower?
Your benchmark is inaccurate. First of all, as you can see, both executions on the database side triggers the same query
SELECT "articles"."id" FROM "articles"
Therefore, the database time should be considered irrelevant. Clearly the two queries had different execution time as shown by the console, but this is normal as if you run the same query 100 times the execution time can be different each time as it depends by a variety of variables such as the machine load, the database state, etc.
Since the database execution time can be considered equivalent, it's irrelevant for the benchmark.
Therefore, what you need to compare is the Ruby execution time and allocation. Pluck is supposed to be faster and more lightweight as compared to collect
it doesn't allocate ActiveRecord objects, rather it returns only the selected values.
If you really want to benchmark the methods, you should mock the database time (which is clearly variable but irrelevant for this benchmark) and only benchmark allocation and the two different Ruby methods.
Long story short, pluck
is generally more efficient.
Rails where condition using NOT NIL
Rails 4+
ActiveRecord 4.0 and above adds where.not
so you can do this:
Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })
When working with scopes between tables, I prefer to leverage merge
so that I can use existing scopes more easily.
Foo.includes(:bar).merge(Bar.where.not(id: nil))
Also, since includes
does not always choose a join strategy, you should use references
here as well, otherwise you may end up with invalid SQL.
Foo.includes(:bar)
.references(:bar)
.merge(Bar.where.not(id: nil))
Rails 3
The canonical way to do this with Rails 3:
Foo.includes(:bar).where("bars.id IS NOT NULL")
Find model records by ID in the order the array of IDs were given
Note on this code:
ids.each do |i|
person = people.where('id = ?', i)
There are two issues with it:
First, the #each method returns the array it iterated on, so you'd just get the ids back. What you want is a collect
Second, the where will return an Arel::Relation object, which in the end will evaluate as an array. So you'd end up with an array of arrays. You could fix two ways.
The first way would be by flattening:
ids.collect {|i| Person.where('id => ?', i) }.flatten
Even better version:
ids.collect {|i| Person.where(:id => i) }.flatten
A second way would by to simply do a find:
ids.collect {|i| Person.find(i) }
That's nice and simple
You'll find, however, that these all do a query for each iteration, so not very efficient.
I like Sergio's solution, but here's another I would have suggested:
people_by_id = Person.find(ids).index_by(&:id) # Gives you a hash indexed by ID
ids.collect {|id| people_by_id[id] }
I swear that I remember that ActiveRecord used to do this ID ordering for us. Maybe it went away with Arel ;)
Find model records by ID in the order the array of IDs were given
Note on this code:
ids.each do |i|
person = people.where('id = ?', i)
There are two issues with it:
First, the #each method returns the array it iterated on, so you'd just get the ids back. What you want is a collect
Second, the where will return an Arel::Relation object, which in the end will evaluate as an array. So you'd end up with an array of arrays. You could fix two ways.
The first way would be by flattening:
ids.collect {|i| Person.where('id => ?', i) }.flatten
Even better version:
ids.collect {|i| Person.where(:id => i) }.flatten
A second way would by to simply do a find:
ids.collect {|i| Person.find(i) }
That's nice and simple
You'll find, however, that these all do a query for each iteration, so not very efficient.
I like Sergio's solution, but here's another I would have suggested:
people_by_id = Person.find(ids).index_by(&:id) # Gives you a hash indexed by ID
ids.collect {|id| people_by_id[id] }
I swear that I remember that ActiveRecord used to do this ID ordering for us. Maybe it went away with Arel ;)
Related Topics
Detecting If This Is an Iframe Load or Direct
Rails:Runtimeerror - Can't Modify Frozen Array When Running Rspec in Rails
How to Convert a Scientific Notation String to Decimal Notation
Ruby Conditional-Assignment and Private Methods
Check If Array of Integers Increments in Ruby
How to Get the Number of Elements Having Same Attribute in HTML in Watir
Ror - How to Remove Rails 4.1.1 Version
Simple Conversion of String to Utf-8 in Ruby 1.8
Rails 4 Activerecord Typeerror Nil Is Not a Symbol
Rails 3 Actionmail Openssl::Ssl::Sslerror
Ruby on Rails Country/State Select Enigma
Rspec 'Eq' VS 'Eql' in 'Expect' Tests
Convert Array-Of-Hashes to a Hash-Of-Hashes, Indexed by an Attribute of the Hashes
Where to Put a Before_Filter Shared Between Multiple Controllers
Ruby on Rails Map.Root Doesn't Seem to Be Working
What's the Best Background Job Management Library for Rails