![](/img/trans.png)
[英]Rails / Active Record has_many through association - fetching a record
[英]Sort based on last has_many record in Rails
我有Student
模型和Gpa
模型。 Student
has_many Gpa
。 我如何根据最近创建的gpa记录的value
属性对学生进行排序?
注意:我不想根据创建的日期对单个学生的GPA进行排序。 我想拉动所有学生并根据他们最近的GPA记录对他们进行排序
class Student < ActiveRecord::Base
has_many :gpas
end
@students = Student.order(...)
假设gpas时间戳是updated_at
Student.joins(:gpas).order('gpas.updated_at DESC').uniq
包括没有gpas的学生
#references is rails 4; works in rails 3 without it
Student.includes(:gpas).order('gpas.updated_at DESC').references(:gpas).uniq
如果你不喜欢uniq
创建的distinct
,你可以使用一些原始的SQL
Student.find_by_sql("SELECT students.* FROM students
INNER JOIN gpas ON gpas.student_id = students.id
LEFT OUTER JOIN gpas AS future ON future.student_id = gpas.student_id
AND future.updated_at > gpas.updated_at
WHERE future.id IS NULL ORDER BY gpas.updated_at DESC")
# or some pretty raw arel
gpa_table = Gpa.arel_table
on = Arel::Nodes::On.new(
Arel::Nodes::Equality.new(gpa_table[:student_id], Student.arel_table[:id])
)
inner_join = Arel::Nodes::InnerJoin.new(gpa_table, on)
future_gpa_table = Gpa.arel_table.alias("future")
on = Arel::Nodes::On.new(
Arel::Nodes::Equality.new(future_gpa_table[:student_id], gpa_table[:student_id]).\
and(future_gpa_table[:updated_at].gt(gpa_table[:updated_at])
)
)
outer_join = Arel::Nodes::OuterJoin.new(future_gpa_table, on)
# get results
Student.joins(inner_join).joins(outer_join).where("future.id IS NULL").\
order('gpas.updated_at DESC')
我不确定是否有办法以任何方便和大多数红宝石的方式实现这一点。 高效实现所需的SQL可能需要基于连接的订单 - 例如......
select
...
from
students
order by
( select gpas.value
from gpas
where gpas.student_id = student.id
order by gpas.as_of_date desc
limit 1)
我不确定这在MySQL中是否合法,但如果它是你可能只是:
Student.order("(select gpas.value from gpas where gpas.student_id = student.id order by gpas.as_of_date desc limit 1)")
另一方面,似乎最后一个值是重要的值,因此您可能希望在gpas上实现回调以在学生表中设置“last_gpa_id”或“last_gpa_value”以使此公共连接更有效。
当然,实施将是微不足道的。
@students = Student.includes(:gpas).order('gpas.value DESC')
值得注意的是,这将包括没有gpas
。 但你可以使用@students.delete_if{ |s| s.gpas.blank? }
轻松过滤掉 @students.delete_if{ |s| s.gpas.blank? }
可能是类似的东西
Student.joins(:gpas).order("gpas.value DESC")
您可以尝试为模型中的关系添加一些选项。
就像是:
class Student < ActiveRecord::Base
has_many :gpas, order: "value DESC", conditions: "foo = bar" #<-whatever conditions you want here
end
class Gpa < ActiveRecord::Base
belongs_to :student
end
使用选项,您只需拨打电话,让Rails完成大部分繁重工作。
如果你感到难过,还有几个选项: http : //guides.rubyonrails.org/association_basics.html
只需在页面上执行关键字搜索“has_and_belongs_to_many关联支持以下选项:”
这应该适合你。 试试这个SQL查询
SELECT * FROM students WHERE id IN
(SELECT student_id
FROM gpa
GROUP BY student_id
ORDER BY created_at DESC);
我想,你可以试试这个方法:
class Student < ActiveRecord::Base
attr_accessible :name
has_many :gpas
def self.by_last_gpas
sql = <<-SQL
select students.*,
(
select gpas.created_at from gpas where student_id=students.id
order by gpas.created_at desc
limit 1
) as last_created_at
from students
order by last_created_at desc
SQL
Student.find_by_sql(sql)
end
end
又快又脏:
你可以以某种方式实现这样的查询来获取你需要的AR对象:
select s.* from students s, gpas g
where s.id = gpas.student_id
and gpas.id in (select max(id) from gpas group by student_id)
order by gpas.value
假设对于具有较高created_at的记录,id更高。
要么
更好的方式:
我想,你经常需要学生的最后一次GPA分数。 为什么不添加一个新列:last_gpa_score
到Student模型?
您可以使用回调来保持此字段的一致性和自动填充,即:
class Gpa < ActiveRecord::Base
belongs_to :student
after_save :update_student_last_score
private
def update_student_last_score
self.student.update_last_gpa_score
end
end
class Student < ActiveRecord::Base
has_many :gpas
def update_last_gpa_score
self.last_gpa_score = self.gpas.order("created_at DESC").first.value
end
end
然后你可以用学生的last_gpa_score字段做任何你喜欢的事情。
您必须调整表名和列名以匹配您的数据库。
SELECT s.*, g.*
FROM (SELECT studentId, MAX(gpaDate) as gpaDate FROM gpas GROUP BY studentId) maxgpa
JOIN gpas g
ON g.studentid = maxgpa.studentId
AND g.gpaDate = maxgpa.gpaDate
JOIN students s
ON s.studentId = g.studentId
ORDER g.gpaDate DESC
创建last_gpa
方法以获取最后创建的GPA,然后执行标准Ruby排序。
def last_gpa
a.gpas.order(:order=>"created_at DESC").first
end
Student.all.sort { |a, b| a.last_gpa <=> b.last_gpa }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.