views:

194

answers:

3

I want to grab the most recent entry from a table. If I was just using sql, you could do

Select top 1 * from table ORDER BY EntryDate DESC

I'd like to know if there is a good active record way of doing this.
I could do something like:

table.find(:order => 'EntryDate DESC').first

But it seems like that would grab the entire result set, and then use ruby to select the first result. I'd like ActiveRecord to create sql that only brings across one result.

A: 

Could just use find_by_sql http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002267

table.find_by_sql "Select top 1 * from table ORDER BY EntryDate DESC"
Guy C
That doesn't actually work with mysql (you've got to use 'limit 1' at the end rather that 'top 1' at the beginning.) I actually started using that before I asked the question because it seemed to me that ActiveRecord should have some native syntactical support for an operation like that. If not, that's all I need to know :)
Daniel
+6  A: 

You need something like:

Model.first(:order => 'EntryDate DESC')

which is shorthand for

Model.find(:first, :order => 'EntryDate DESC')

Take a look at the documentation for first and find for details.

Stephen Veiss
+1  A: 

The Rails documentation seems to be pretty subjective in this instance. Note that .first is the same as find(:first, blah...)

From:http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002263

"Find first - This will return the first record matched by the options used. These options can either be specific conditions or merely an order. If no record can be matched, nil is returned. Use Model.find(:first, args) or its shortcut Model.first(args)."

Digging into the ActiveRecord code, at line 1533 of base.rb (as of 9/5/2009), we find:

    def find_initial(options)
      options.update(:limit => 1)
      find_every(options).first
    end

This calls find_every which has the following definition:

    def find_every(options)
      include_associations = merge_includes(scope(:find, :include), options[:include])

      if include_associations.any? && references_eager_loaded_tables?(options)
        records = find_with_associations(options)
      else
        records = find_by_sql(construct_finder_sql(options))
        if include_associations.any?
          preload_associations(records, include_associations)
        end
      end

      records.each { |record| record.readonly! } if options[:readonly]

      records
    end

Since it's doing a records.each, I'm not sure if the :limit is just limiting how many records it's returning after the query is run, but it sure looks that way (without digging any further on my own). Seems you should probably just use raw SQL if you're worried about the performance hit on this.

MattC
The :limit option is passed through to SQL, so there's no performance hit from loading all the records into Rails and filtering there. The easiest way to see what a query is doing is to tail development.log while executing queries on the console.
Stephen Veiss
After I read my answer again I figured that the :limit => 1 option was probably being passed all the way down into the raw SQL that was being build, but I didn't have the time to keep researching and didn't want to gamble by making an incorrect assertion on the behavior :)
MattC