Rails ActiveRecord加入范围内的最近一个孩子

时间:2016-01-27 19:22:59

标签: ruby-on-rails ruby activerecord rails-activerecord

我目前正在尝试编写一名工作人员,该工作人员必须检查最近孩子的状态并根据该状态对其进行排队。例如,找到年龄最大的孩子在校的父母

class Parent
  has_many :children
end
class Child
  belongs_to :parent
  lookup_for :status, symbolize: true
end

目前我的范围是:

Parent.joins(:children).
      where("children.birth_date =
            (SELECT MAX(children.birth_date)
             FROM children
             WHERE children.parent_id = parents.parent_id").
      where('children.status = ?', Status[:in_school]').pluck(:parent_id)

似乎应该有更好的方法来做到这一点。任何想法

澄清我正在寻找所有仍在上学的父母

4 个答案:

答案 0 :(得分:0)

如果您在查询中经常使用第一个/或最后一个孩子,我会将它们添加为父项中的字段

class Parent
  belongs_to :youngest_child, class_name: Child, inverse_of: :parent
end

答案 1 :(得分:0)

好吧,您可以将一些逻辑推送到如下所示的joins()语句中,但我不知道如何在某处获得子查询。

Parent.joins(%Q{left join children on children.parent_id = parents.id
                  and children.birth_date = (select max(birth_date) from children 
                  where children.parent_id = parents.id)}).
                where('children.status = ?', Status[:in_school]')

那就像那样。 HTH

答案 2 :(得分:0)

或者,您可以使用order和last查找最后一个子项,然后检查子项内存中的状态。这种方法最多只会有一个孩子,所以表现合理。如下所示:

child=Child.order(:birth_date).joins(:parent).last
parent.do_stuff if child.in_school?

答案 3 :(得分:0)

您的查询有效 - 它完成了工作,很明显您的目标是什么。我在我正在处理的应用中测试了您的模式,并且ActiveRecord将所有内容合并到一个查询中,并且 1.4ms

如果要优化性能,可以使用连接来获取最老的子节点,而不是子查询:

Parent.joins("INNER JOIN (
                SELECT c1.*
                FROM children c1
                LEFT JOIN children c2 ON (c1.parent_id = c2.parent_id AND c1.birthdate > c2.birthdate)
                WHERE c2.parent_id IS NULL
              ) c ON parent.parent_id = c.parent_id")
              .where('children.status = ?', Status[:in_school])
              .pluck(:parent_id)

你的目标有点不太明确,因为它使用了一种手动外连接,但它允许嵌套连接也使用索引。在与上面相同的测试场景中,这在 0.9ms 中执行相同的查询(几乎快两倍)。那是一个非常小的数据库,只有几百条记录。凭借数百万条记录,差异将是显而易见的。

感谢这个StackOverflow answer,对于连接模式。