Извлечение минимума / максимума для каждой группы в ActiveRecord - PullRequest
11 голосов
/ 09 октября 2008

Это старый вопрос, для которого задана таблица с атрибутами «тип», «разнообразие» и «цена», когда вы выбираете запись с минимальной ценой для каждого типа.

В SQL мы можем сделать this :

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;`

Возможно, мы могли бы подражать этому:

minprices = Table.minimum(:price, :group => type)  
result = []
minprices.each_pair do |t, p|  
   result << Table.find(:first, :conditions => ["type = ? and price = ?", t, p])
end

Есть ли лучшая реализация, чем эта?

Ответы [ 4 ]

12 голосов
/ 09 октября 2008
Table.minimum(:price, :group => :type)

Подробнее см. http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum.

0 голосов
/ 10 июня 2014

Чтобы обновить ответ Авди выше:

Table.minimum (: цена,: группа =>: тип)

Вот обновленный URL:

http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum

0 голосов
/ 14 февраля 2013

Я боролся с этим некоторое время, и на данный момент кажется, что вы в значительной степени застряли в генерировании SQL.

Однако у меня есть пара усовершенствований.

Вместо find_by_sql, как предложил @ Франсуа, я использовал ActiveRecord to_sql и joins, чтобы немного "навести" мой SQL:

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>)

Как видите, я все еще использую сырой SQL, но, по крайней мере, только в той части, где AR не поддерживает (AFAIK ActiveRecord просто не может управлять INNER JOIN ... ON ...), а не в целом.

Использование joins вместо find_by_sql делает запрос цепным - вы можете добавить дополнительные условия, или отсортировать таблицу, или поместить все в область действия.

0 голосов
/ 09 октября 2008

Вы можете использовать # find_by_sql, но это подразумевает возврат объекта модели, который может быть не тем, что вы хотите.

Если вы хотите обнажить металл, вы также можете использовать # 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 - это просто инструмент. Вы используете это, когда это удобно. Когда SQL работает лучше, вы используете это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...