活动记录 - 按 child 属性排序 parent

active record - sort parent by by child attributes

活动记录非常新。

我正在尝试使用活动记录来查询 'changes' 并按最接近 children 的 start_time 排序(ctasks 和 legacy_ctasks)。

如果更改有 ctasks 和 legacy_ctasks,排序时使用最早(最小)start_time。

一项更改必须至少有一个 ctask 或 legacy_ctask。

一个更改可以有很多 ctasks and/or 许多 legacy_ctasks

ctasks 和 legacy_ctasks 都有一个 start_time 字段:

t.datetime "start_time":

型号:

class Change < ApplicationRecord
    has_many :ctasks, -> { order "start_time ASC" }, dependent: :destroy
    has_many :legacy_ctasks, -> { order "start_time ASC" }, dependent: :destroy
end

class LegacyCtask < ApplicationRecord
  belongs_to :change, touch: true
end

class Ctask < ApplicationRecord
  belongs_to :change, touch: true
end

我现在的尝试很伤心:

Change.left_joins(:ctasks, :legacy_ctasks).order('ctasks.start_time ASC').order('legacy_ctasks.start_time ASC').distinct

按两个关联 table 中任一个的最大值排序有点粗糙。根据您执行此操作的频率和您的 performance/space 需求,您可以考虑在 changes table 上缓存时间戳,以便您可以轻松地对其进行索引。

但我认为您可以采取几种方法。一种是同时加入 table 并按 changes.id 分组。我认为应该看起来像这样:

Change.select("changes.*")
  .group("changes.id")
  .left_joins(:ctasks, :legacy_ctasks)
  .order("GREATEST(MAX(ctasks.start_time),MAX(legacy_ctasks.start_time))")

另一种选择是对每个 table 使用横向连接以从每个中提取最早的记录。这在 AR 中并不那么容易,但应该更高效,类似于:

ctasks_joins_sql = <<-SQL
LEFT OUTER JOIN LATERAL (
  SELECT ctasks.start_time FROM ctasks WHERE ctasks.change_id = changes.id 
  ORDER BY start_time
  LIMIT 1
) as ctasks ON true
SQL
legacy_ctasks_join_sql = <<-SQL
LEFT OUTER JOIN LATERAL (
  SELECT legacy_ctasks.start_time FROM legacy_ctasks WHERE legacy_ctasks.change_id = changes.id 
  ORDER BY start_time
  LIMIT 1
) as legacy_ctasks ON true
SQL

Change.select("changes.*")
  .joins(ctasks_joins_sql)
  .joins(legacy_ctasks_join_sql)
  .order("GREATEST(ctasks.start_time, legacy_ctasks.start_time)")