问题
This is an age-old question where given a table with attributes 'type', 'variety' and 'price', that you fetch the record with the minimum price for each type there is.
In SQL, we can do this by:
select f.type, f.variety, f.price
from ( select type, min(price) as minprice from table group by type ) as x
inner join table as f on f.type = x.type and f.price = x.minprice;`
We could perhaps imitate this by:
minprices = Table.minimum(:price, :group => type)
result = []
minprices.each_pair do |t, p|
result << Table.find(:first, :conditions => ["type = ? and price = ?", t, p])
end
Is there a better implementation than this?
回答1:
Table.minimum(:price, :group => :type)
See http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum for more.
回答2:
You can use #find_by_sql, but this implies returning a model object, which might not be what you want.
If you want to go bare to the metal, you can also use #select_values:
data = ActiveRecord::Base.connection.select_values("
SELECT f.type, f.variety, f.price
FROM (SELECT type, MIN(price) AS minprice FROM table GROUP BY type ) AS x
INNER JOIN table AS f ON f.type = x.type AND f.price = x.minprice")
puts data.inspect
[["type", "variety", 0.00]]
ActiveRecord is just a tool. You use it when it's convenient. When SQL does a better job, you use that.
回答3:
I've been fighting with this for a while and for the moment it seems that you are pretty much stuck with generating SQL.
However, I have a couple refinements to offer.
Instead of find_by_sql
, as @François suggested, I've used ActiveRecord's to_sql
and joins
to "guide" my SQL a little bit:
subquery_sql = Table.select(["MIN(price) as price", :type]).group(:type).to_sql
joins_sql = "INNER JOIN (#{subquery_sql}) as S
ON table.type = S.type
AND table.price = S.price"
Table.joins(joins_sql).where(<other conditions>).order(<your order>)
As you can see, I'm still using raw SQL, but at least it's only on the part where AR gives no support (AFAIK ActiveRecord simply can't manage INNER JOIN ... ON ...
) and not on the whole thing.
Using joins
instead of find_by_sql makes the query chainable - you can add extra conditions, or sort the table, or put everything in a scope.
回答4:
To update Avdi's answer above:
Table.minimum(:price, :group => :type)
Here is the updated URL:
http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum
来源:https://stackoverflow.com/questions/185569/fetching-minimum-maximum-for-each-group-in-activerecord