【问题标题】:How To Order ActiveRecord Relation Based on Column Of Last Record Of Associated Object如何根据关联对象的最后记录列对 ActiveRecord 关系进行排序
【发布时间】:2016-09-14 08:04:52
【问题描述】:

我有以下两种型号:

Client
has_many :cases

Case
belongs_to :client

假设我有 2 个客户 c1 和 c2。
c1 有 1 例,c2 有 2 例。

c1    = Client.create(name: 'C1')
c2    = Client.create(name: 'C2')
case1 = Case.create(type: 'Normal', client: c1)
case2 = Case.create(type: 'Normal', client: c2)
case3 = Case.create(type: 'High', client: c2)

我想获取所有客户端并按每个客户端 asc/desc 的最后一个案例类型排序。 所以如果我按 asc 排序,我应该得到 'c1' 然后是 c2 的结果。如果我按 desc 排序,我应该得到 'c2' 然后 'c1' 的结果。以下是我迄今为止尝试过的。

Client.includes(:cases).order('cases.type asc') # => 'C2,C1'
Client.includes(:cases).order('cases.type desc') # => 'C2,C1'

由于 c2 也有一个类型为“正常”的案例,我认为查询似乎是先找到客户端,然后执行类似于 client.cases 的操作。然后,我认为它只在每个客户端下订购案例,而不是跨客户端订购案例。
我觉得我的解释有点混乱。如果你有,请给我一个问题。谢谢。

【问题讨论】:

  • 您是否尝试过使用空数据库的示例?我认为它适用于 rails 4。可能发生的情况是您在两种情况下都会返回 c2,c1,因为未定义具有相同 type 的记录的顺序。您是否将type 用于 STI?当您将名称为 type 的列用于 STI 以外的其他目的时,可能会产生不必要的副作用。
  • 感谢@slowjack2k 的评论。我已经更新了返回结果,因为这是我之前的错误。无论如何,我的问题是我按升序或降序排序,结果是一样的。另一方面,感谢您提到使用“类型”作为列名的副作用。实际上,我的列名是“case_type”,但我只是说类型作为示例,而我忘记了这一点。无论如何,我不会将它用于 STI。

标签: sql ruby-on-rails activerecord queryinterface


【解决方案1】:

我已更改列“case_type”而不是“type”,因为我们不能在表中使用列类型。

要按客户案例的最后记录的案例类型对数据进行排序,请试试这个。

对于 ASC

Client.includes(:cases).sort_by{|m| m.cases.last.case_type}

对于 DESC

 Client.includes(:cases).sort_by{|m| m.cases.last.case_type}.reverse

【讨论】:

  • 感谢@Prashant 的回答。实际上,我已经尝试过您的选择,但是当某些客户根本没有案例时,我得到了一个错误。有办法解决这个问题吗?
  • Client.all.sort_by{|m| m.cases.present? ? m.cases.last.case_type : "ZZZ"}
【解决方案2】:

您可以像这样为有很多关联设置默认排序器:

has_many :cases, -> { order 'type asc' }

这样,箱子将始终按顺序返回。因此,您可以使用 sort_by 在您的客户上设置自定义排序,调用最后一个案例(并确信这些案例是有序的),例如:

Client.includes(:cases).sort_by { |c| c.cases.last.type }

【讨论】:

  • 感谢@Joe 的回答。实际上,我已经尝试过您的第二个选项,但是当某些客户根本没有案例时,我得到了一个错误。有办法解决这个问题吗?
【解决方案3】:

在我看来,一切都很好。 您的订单声明仅确保typecase_type 的顺序。 数据库可以返回

Normal, C1
Normal, C2
High, C2

Normal, C2
Normal, C1
High, C2

cases.type asc

type 等于您未指定时会发生什么。 Rails 将按照数据库返回记录的顺序返回您的对象。

如果你不想给你另一个订单,你必须指定订单,例如:

Client.includes(:cases).order('cases.type asc, clients.name asc')

编辑:

要仅对最后一条相关记录进行排序,您可以使用子选择:

Client.select('(SELECT cases.type from cases where cases.client_id=clients.id ORDER BY cases.id desc LIMIT 1) as case_type_name, *').order('case_type_name asc')

【讨论】:

    猜你喜欢
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    • 2018-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 1970-01-01
    相关资源
    最近更新 更多