【问题标题】:Rails order by attribute of last record from has_many associationRails 按来自 has_many 关联的最后一条记录的属性排序
【发布时间】:2021-08-24 23:28:20
【问题描述】:

我有两个具有 belongs_to 和 has_many 关系的模型。 BreedCycle 具有breed_start_date 日期时间属性。我想做的是通过最新(最后创建的)品种周期的breed_start_date 属性订购boxes。长话短说,我的目标是编写查询,通过breed_cycles.breed_start_date 属性为盒子和订单盒子提取所有最新的品种循环。 breed_start_date 也可以为零,所以我需要 NULLS LAST 选项。非常感谢任何帮助(文档链接、示例等)。

class Box < ApplicationRecord
  has_many :breed_cycles
end

class BreedCycle < ApplicationRecord
  belongs_to :box
end

【问题讨论】:

    标签: ruby-on-rails postgresql


    【解决方案1】:
    Box.left_joins(:breed_cycles)
       .group(:id) 
       .order('MAX(breed_cycles.breed_start_date) DESC NULLS LAST')
    

    或者Arel in Rails 6.1+:

    Box.left_joins(:breed_cycles)
       .group(:id)
       .order(BreedCycle.arel_table[:breed_start_date].maximum.desc.nulls_last)
    

    【讨论】:

    • 好答案。对于 PG,我认为横向性能更高,但我喜欢这种方式是为了清晰/简单……但老实说,如果需要任何其他连接,我会担心规模化/使用的任何一种方法。如果性能是一个问题,我会考虑在主表上缓存日期,以便可以直接对其进行索引。
    • @melcher 我很想看看横向解决方案是什么样的。我还没有完全理解横向连接。
    【解决方案2】:

    添加 lateral-join 选项作为 Max 答案的扩展。

    所以 - 横向连接是 postgreSQL 支持的连接类型,它是 like a correlated sub-query,但性能更高。我最常将它用于像这样的情况 - 我有一个“有很多”但我只想加入一个。从功能上讲,它们的运行方式与您期望的“for-loop”运行方式一样 - 对于您要连接的表中的每一行,执行横向连接并添加返回的任何行。

    无论如何,这种情况下的横向连接 SQL 看起来像:

    SELECT * from boxes 
    LEFT JOIN LATERAL ( 
      SELECT breed_cycles.breed_start_date 
      FROM breed_cycles 
      ORDER BY breed_start_date DESC 
      LIMIT 1
    ) AS last_breed_cycle ON true
    ORDER BY last_breed_cycle.breed_start_date DESC NULLS LAST
    

    把它变成 activerecord 就像这样:

    lateral_join_sql = <<-SQL-
    LEFT JOIN LATERAL ( 
      SELECT breed_cycles.breed_start_date 
      FROM breed_cycles 
      ORDER BY breed_start_date DESC 
      LIMIT 1
    ) AS last_breed_cycle ON true
    -SQL
    Box.joins(lateral_join_sql).order('last_breed_cycle.breed_start_date DESC')
    

    我几乎从不使用 Arel,所以我将把它作为练习留给读者...

    【讨论】:

      【解决方案3】:

      试试这个。我没有运行 PG 我无法测试 nulls last 部分,但我相信你可以推断...

      Box.joins(:breed_cycles).
          select("boxes.*, max(breed_cycles.created_at) as most_recent_breed_cycle").
          group("breed_cycles.box_id").
          order("most_recent_breed_cycle")
      

      它能满足你的需要吗?

      【讨论】:

      • 您好,我已经尝试了您的查询,其中的一部分,并进行了一些修改,但我没有得到任何结果
      • 你的数据好吗?你会得到什么查询(在 Rails 控制台中)Box.first.breed_cycles.map(&amp;:created_at)
      • [2021 年 8 月 24 日星期二 16:51:22.895868000 UTC +00:00,2021 年 8 月 24 日星期二 16:51:22.904614000 UTC +00:00,2021 年 8 月 24 日星期二 16:51: 22.913040000 UTC +00:00,星期二,2021 年 8 月 24 日 16:51:22.920427000 UTC +00:00,星期二,2021 年 8 月 24 日 16:51:22.928809000 UTC +00:00] 这就是我得到的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-29
      相关资源
      最近更新 更多