Это давний вопрос, когда из таблицы с атрибутами «тип», «разнообразие» и «цена» вы получаете запись с минимальной ценой для каждого типа.
В SQL мы можем выполнить это следующим образом:
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
Есть ли лучшая реализация, чем эта?





Table.minimum(:price, :group => :type)
Подробнее см. http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum.
как получить максимальную и минимальную цену для каждого вида ??
По какой-то причине часть :group => :type для меня игнорируется.
Вы можете использовать # 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 работает лучше, вы его используете.
Я боролся с этим некоторое время, и на данный момент кажется, что вы в значительной степени застряли с генерацией SQL.
Тем не менее, я могу предложить несколько уточнений.
Вместо find_by_sql, как предложил @ François, я использовал 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 делает запрос цепочкой - вы можете добавить дополнительные условия, отсортировать таблицу или поместить все в область видимости.
Чтобы обновить ответ Авди выше:
Table.minimum(:price, :group => :type)
Вот обновленный URL:
http://api.rubyonrails.org/classes/ActiveRecord/Calculations.html#method-i-minimum
Возможно, вам будет полезна ссылка на более свежую версию Rails. Не уверен, почему вы выбрали ссылку на две основные версии назад.
Спасибо, Исаак. Обновлено.
Хотя этот вопрос довольно устаревший, я задавал тот же вопрос сегодня. Вот суть решения для составления SQL, необходимого для достижения цели с помощью минимальных (2) запросов.
Пожалуйста, посмотрите, есть ли в наши дни способы получше!
Используя модели Security и Price, где Ценные бумаги имеют много (исторических) цен, и вы следите за самой последней ценой Ценных бумаг:
module MostRecentBy
def self.included(klass)
klass.scope :most_recent_by, ->(group_by_col, max_by_col) {
from(
<<~SQL
(
SELECT #{table_name}.*
FROM #{table_name} JOIN (
SELECT #{group_by_col}, MAX(#{max_by_col}) AS #{max_by_col}
FROM #{table_name}
GROUP BY #{group_by_col}
) latest
ON #{table_name}.date = latest.#{max_by_col}
AND #{table_name}.#{group_by_col} = latest.#{group_by_col}
) #{table_name}
SQL
)
}
end
end
class Price < ActiveRecord::Base
include MostRecentBy
belongs_to :security
scope :most_recent_by_security, -> { most_recent_by(:security_id, :date) }
end
class Security < ActiveRecord::Base
has_many :prices
has_one :latest_price,
-> { Price.most_recent_by_security },
class_name: 'Price'
end
теперь вы можете вызвать в коде контроллера следующее:
def index
@resources = Security.all.includes(:latest_price)
render json: @resources.as_json(include: :latest_price)
end
что приводит к двум запросам:
Security Load (4.4ms) SELECT "securities".* FROM "securities"
Price Load (140.3ms) SELECT "prices".* FROM (
SELECT prices.*
FROM prices JOIN (
SELECT security_id, MAX(date) AS date
FROM prices
GROUP BY security_id
) latest
ON prices.date = latest.date
AND prices.security_id = latest.security_id
) prices
WHERE "prices"."price_type" = AND "prices"."security_id" IN (...)
для справки: https://gist.github.com/pmn4/eb58b036cc78fb41a36c56bcd6189d68
Это сработало для меня.
Table.group(:type).minimum(:price)
И он возвращает такой объект.
{
"type1"=>500.0,
"type2"=>200.0
}
как получить максимальную и минимальную цену для каждого вида ??