首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ActiveRecord:如何在每组最多的情况下订购和检索记录

ActiveRecord:如何在每组最多的情况下订购和检索记录
EN

Stack Overflow用户
提问于 2022-11-04 19:12:59
回答 2查看 55关注 0票数 0

我被一个经典的greatest-n-per-group问题困住了,猫可以养很多小猫,但我通常只对最小的小猫感兴趣。

我已经知道如何为scope构建一个has_one关系和一个Cat关系。

我的问题是:有没有办法.

  1. 列出所有猫的名字和它们最小的小猫的名字..。
  2. 同时点了他们各自最小的小猫的名字.

...using只是在引擎盖下的一个选择?

到目前为止我得到的是:

代码语言:javascript
复制
class Cat < ApplicationRecord
  has_many :kittens

  has_one :youngest_kitten, -> { merge(Kitten.youngest) }, foreign_key: :cat_id, class_name: :Kitten

  scope :with_youngest_kittens, lambda {
                                joins(:kittens)
                                  .joins(Kitten.younger_kittens_sql("cats.id"))
                                  .where(younger_kittens: { id: nil })
                              }
end

class Kitten
  belongs_to :cat
  scope :youngest, lambda {
    joins(Kitten.younger_kittens_sql("kittens.cat_id"))
      .where(younger_kittens: { id: nil })
  }

  def self.younger_kittens_sql(cat_field_name)
    %{
      LEFT OUTER JOIN kittens AS younger_kittens
      ON younger_kittens.cat_id = #{cat_field_name}
        AND younger_kittens.created_at > kittens.created_at
    }
  end
end

当我运行Cat.with_latest_kittens.order('kittens.name').map(&:name)时,一切看起来都很好:我只需要一个选择就可以得到所有猫的名字。

但是当我运行Cat.with_latest_kittens.order('kittens.name').map {|cat| cat.youngest_kitten.name}时,我也得到了正确的结果,但是每一个cat执行一个多余的额外SELECT。这是合乎逻辑的,因为with_youngest_kittens不知道它应该填充youngest_kitten。有什么办法告诉你吗?还是我说错了?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-11-05 09:17:31

我认为将包括添加到:with_youngest_kittens范围将解决问题。尝试将范围更改为

代码语言:javascript
复制
  scope :with_youngest_kittens, lambda {
    includes(:youngest_kitten)
      .joins(:kittens)
      .joins(Kitten.younger_kittens_sql("cats.id"))
      .where(younger_kittens: { id: nil })
  }

这应该会阻止Rails为每个小程序单独进行数据库查询。

票数 1
EN

Stack Overflow用户

发布于 2022-11-05 17:18:35

我找到了一个解决方案,它不会产生额外的选择,但是它是相当丑陋的,所以我实际上会选择局部箭头解,因为它更易读!

为了完整起见,我想我还是会发布它(如果有人需要额外的几个ms性能):

首先,我为每个kitten列添加定制的选择字段到Cat.with_youngest_kitten范围:

代码语言:javascript
复制
scope :with_youngest_kittens, lambda {
                                kitten_columns = Kitten
                                  .column_names
                                  .map { |column_name| "kittens.#{column_name} AS `youngest_kittens.#{column_name}`" }
                                  .join(', ')

                                joins(:kittens)
                                  .joins(Kitten.latest_outer_join_sql("cats.id"))
                                  .where(later_kittens: { id: nil })
                                  .select("cats.*, #{kitten_columns}")
                              }

然后,我用一个方法覆盖has_one youngest_kitten关系,该方法检索那些自定义的选择,并在没有检索数据的情况下调用超级:

代码语言:javascript
复制
def youngest_kitten
    return super if self[:'youngest_kittens.id'].nil?

    kitten_hash = Hash[Kitten.column_names.collect { |column_name| [column_name, self[:"youngest_kittens.#{column_name}"]] }]
    kitten_hash[:cat] = self
    Kitten.new(kitten_hash)
end
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74321945

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档