While this question is pretty stale, I was asking the same question today.
Here's a gist of a solution to compose the SQL needed to accomplish the goal with minimal (2) queries.
Please lmk if there are better ways these days!
Using Security
and Price
models, where Securities have many (historical) Prices, and you are after the Securities' most recent 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
now you can call the following in your controller code:
def index
@resources = Security.all.includes(:latest_price)
render json: @resources.as_json(include: :latest_price)
end
which results in two queries:
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 (...)
for reference: https://gist.github.com/pmn4/eb58b036cc78fb41a36c56bcd6189d68