活动记录-按子属性对父记录排序



活动记录新功能

我试图使用活动记录来查询'更改',并按子女(ctasks和legacy_ctasks)的最近start_time排序。

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

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

一个更改可以有多个ctasks和/或多个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

按照两个关联表中的最大值排序有点粗糙。根据您执行此操作的频率和您的性能/空间需求,您可以考虑缓存changes表上的时间戳,以便您可以轻松地通过它建立索引。

但我认为你可以采取几种方法。一种方法是通过changes.id连接表和组。我觉得应该是这样的:

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

另一种选择是对每个表使用横向连接,从每个表中提取最早的记录。这在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)")

最新更新