1

A:parent has_many :children我正在尝试检索父母的最大孩子的年龄作为parent. 我愿意接受任何有效地实现这一目标的解决方案。

我试图做一个子查询的原因是让数据库做n+1开销,而不是为每个父母发出单独的数据库请求。两者都是低效的,但使用子查询似乎更有效。

# attributes: id
class Parent < ActiveRecord::Base
  has_many :children

  # Results in an (n+1) request
  def age_of_oldest_child
    children.maximum(:age)
  end
end

# attributes: id, parent_id, age
class Child < ActiveRecord::Base
  belongs_to :parent
end

示例用例:

parent = Parent.first.age_of_oldest_child # => 16

parents = Parent.all
parents.each do |parent|
  puts parent.age_of_oldest_child # => 16, ...
end

我的尝试:

sql = "
  SELECT 
    (SELECT
      MAX(children.age)
      FROM children
      WHERE children.parent_id = parents.id
    ) AS age_of_oldest_child
  FROM
    parents;
"

Parent.find_by_sql(sql)

这将返回所有父母的最大年龄数组;我想将其限制为只有 1 个父级,或者在检索所有父级时将其作为父级的属性包含在内。

2015-06-19 11:00 更新

这是我想出的一个可行的解决方案;有更有效的选择吗?

class Parent < ActiveRecord::Base
  scope :with_oldest_child, -> { includes(:oldest_child) }

  has_many :children
  has_one :oldest_child, -> { order(age: :desc).select(:age, :parent_id) }, class_name: Child

  def age_of_oldest_child
    oldest_child && oldest_child.age
  end
end

示例用法:

# 2 DB queries, 1 for parent and 1 for oldest_child
parent = Parent.with_oldest_child.find(1)

# No further DB queries
parent.age_of_oldest_child # => 16
4

1 回答 1

0

这里有两种方法:

父.rb

class Parent < ActiveRecord::Base
  has_many :children

  # Leaves choice of hitting DB up to Rails
  def age_of_oldest_child_1
    children.max_by(&:age)
  end

  # Always hits DB, but avoids instantiating Child objects
  def age_of_oldest_child_2
    Child.where(parent: self).maximum(:age)
  end
end

第一种方法使用可枚举模块的max_by功能并调用age集合中的每个对象。这样做的好处是您将是否访问数据库的逻辑留给 Rails。如果由于children某种原因已经实例化,它将不会再次访问数据库。如果它们没有被实例化,它将执行一个选择查询,在单个查询中将它们加载到内存中(从而避免 N+1),然后遍历每个调用它的age方法。

然而,两个缺点是,如果在子实例化后底层数据发生了变化,它仍然会使用过时的结果(这可以通过:true在调用时传递来避免:children。此外,它首先将每个数据加载child到内存中,然后计数它们。如果child对象很大和/或父对象有大量子对象,则可能会占用大量内存。这实际上取决于您的用例。

如果您决定要避免加载所有这些children,则可以每次使用count方法 2 中描述的查询进行直接 DB 命中。事实上,您可能实际上希望将其重新定位到一个范围内Child,也许有些人会考虑它在目标模型之外执行类似查询的反模式,但这只是为了更容易查看示例。

于 2015-06-18T17:37:03.893 回答