Last Updated: February 25, 2016
·
825
· pboling

Never use ActiveRecord's last

Performance - Your argument is invalid

When there is not a reason to prefer one over the other :first will generally be faster than :last, because last invokes an implicit ORDER.
Granted on a table with only tens of thousands of rows it is not a big deal, on the order of tenths of milliseconds (after warming up my DB with dozens of iterations):

[26] pry(main)> User.first.id
  User Load (1.4ms)  SELECT "users".* FROM "users" LIMIT 1
=> 35811
[27] pry(main)> User.last.id
  User Load (1.6ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
=> 36792

But none of this even matters, because nothing can justify...

How to explode in 5 steps

  • Create a scope:
class Lead < ActiveRecord::Base
  scope :referred_to_pool, -> { 
    where("leads.state in (?)", ["cold_pool","hot_pool"]) }
end
  • Build a feature that relies on the scope and uses last:
class ReferredLeadsController < ApplicationController
  def next_to_assign
    @lead = Lead.referred_to_pool.last
  end
  • Decide you want to handle the assignments in a prioritized order, so update the scope
scope :referred_to_pool, -> { 
  where("leads.state in (?)", ["cold_pool","hot_pool"]).
  order("referred_at DESC NULLS LAST") }
  • Test that the scope works, and can even be used with first
Lead.referred_to_pool.order("assigned_at DESC NULLS LAST").first
  Lead Load (91.1ms)  SELECT "leads".* FROM "leads"  WHERE (leads.state in ('cold_pool','hot_pool')) ORDER BY referred_at DESC NULLS LAST LIMIT 1
=> #<Lead id:...
  • But you used last, so instead watch your app explode:
  Lead Load (0.8ms)  SELECT "leads".* FROM "leads"  WHERE (leads.state in ('cold_pool','hot_pool')) ORDER BY referred_at DESC NULLS LAST DESC LIMIT 1
ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:  syntax error at or near "DESC"
LINE 1: ...utfitter_id) ORDER BY referred_at DESC NULLS LAST DESC LIMIT...
                                                             ^
: SELECT  "leads".* FROM "leads" WHERE (leads.state in ('cold_pool','hot_pool')) ORDER BY referred_at DESC NULLS LAST DESC LIMIT 1
from .../activerecord-3.2.21/lib/active_record/connection_adapters/postgresql_adapter.rb:1163:in `async_exec'