虽然这个问题很陈旧,但我今天问了同样的问题。下面是一个解决方案的要点,它可以用最少 (2) 个查询来组合实现目标所需的 SQL。
如果这些天有更好的方法,请lmk!
使用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" = $1 AND "prices"."security_id" IN (...)
供参考:https ://gist.github.com/pmn4/eb58b036cc78fb41a36c56bcd6189d68