我已经为此寻找了一段时间......我无法找到优雅的解决方案。我有loans
和loans has_many :decisions
。 decisions
有一个我关心的属性,称为risk_rating
。
我希望根据最近的决定(基于created_at,按常规)排序loans
,但是risk_rating
。
Loan.includes(:decisions).references(:decisions).order('decisions.risk_rating DESC')
不起作用......
我想要贷款......按照他们最近的决定风险排序。这似乎应该比它更容易。
我目前正在这样的数据库之外这样做,但它正在咀嚼时间和记忆:
Loan.all.sort do |x,y|
x.decisions.last.try(:risk_rating).to_f <=> y.decisions.last.try(:risk_rating).to_f
end
我希望展示我对拟议答案的表现以及不准确的表现......
Benchmark.bm do |x|
x.report{ Loan.joins('LEFT JOIN decisions ON decisions.loan_id = loans.id').group('loans.id').order('MAX(decisions.risk_rating) DESC').limit(10).map{|l| l.decisions.last.try(:risk_rating)} }
end
user system total real
0.020000 0.000000 0.020000 ( 20.573096)
=> [0.936775, 0.934465, 0.932088, 0.922352, 0.921882, 0.794724, 0.919432, 0.918385, 0.916952, 0.914938]
订单不对。那0.794724
不合适。
就此而言......我只是在建议的答案中看到一个属性。我没有看到连接= /
答案 0 :(得分:2)
好吧,看起来我今晚工作到很晚,因为我忍不住跳了进去:
class Loan < ApplicationRecord
has_many :decisions
has_one :latest_decision, -> { merge(Decision.latest) }, class_name: 'Decision'
end
class Decision < ApplicationRecord
belongs_to :loan
def latest
t1 = arel_table
t2 = arel_table.alias('t2')
# Self join based on `loan_id` prefer latest `created_at`
join_on = t1[:loan_id].eq(t2[:loan_id]).and(
t1[:created_at].lt(t2[:created_at]))
where(t2[:loan_id].eq(nil)).joins(
t1.create_join(t2, t1.create_on(join_condition), Arel::Nodes::OuterJoin)
)
end
end
Loan.includes(:latest_decision)
这不排序,只提供每笔贷款的最新决定。由于表别名,抛出引用order
的{{1}}会使事情变得混乱。我现在没有时间解决这个问题,但我敢打赌,如果你查看Arel上的一些优秀资源以及如何将它与ActiveRecord一起使用,你可以弄明白。我真的很喜欢this one。
答案 1 :(得分:1)
首先让我们编写sql-query,它将选择必要的数据。 SO包含一个可能有帮助的问题:Select most recent row with GROUP BY in MySQL。我最好的版本:
SELECT loans.*
FROM loans
LEFT JOIN (
SELECT loan_id, MAX(id) as id
FROM decisions
GROUP BY loan_id) d ON d.loan_id = loans.id
LEFT JOIN decisions ON decisions.id = d.id
ORDER BY decisions.risk_rating DESC
此代码假设MAX(id)
给出了组中最近一行的id
。
您可以通过此Rails代码执行相同的查询:
sub_query =
Decision.select('loan_id, MAX(id) as id').
group(:loan_id).to_sql
Loan.
joins("LEFT JOIN (#{sub_query}) d ON d.loan_id = loans.id").
joins("LEFT JOIN decisions ON decisions.id = d.id").
order("decisions.risk_rating DESC")
不幸的是,我手头没有MySQL,我无法试用这段代码。希望它能奏效。