【问题标题】:Rails 4: Odd Activerecord sort orderRails 4:奇数 Activerecord 排序顺序
【发布时间】:2014-03-19 18:43:58
【问题描述】:

就像this question 一样,我希望急切加载另一个对象,然后根据急切加载的对象中的值对列表进行排序。

给定父母Foo和孩子Bar

class Foo < ActiveRecord::Base
  has_many :bar, foreign_key: :foo_name, primary_key: :name   
  has_one  :most_recent_bar, -> { order created at desc }, foreign_key: :foo_name, primary_key: :name   
end

class Bar < ActiveRecord::Base
  belongs_to :foo, foreign_key: :foo_name, primary_key: :name   
end

我希望显示Foos 的列表,然后按Bar.valueFoo.name 对它们进行排序。我只是在寻找最近创建的Bar,所以它以相反的顺序排序并且限制为1。所以:

@foos = Foo.includes(:most_recent_bar).order('bar.value asc, foo.name asc')   

问题:上面的订单声明正在改变most_recent_bar。它对所有Foo.bars 进行排序,然后选择排序列表中最新的。只有这样才能通过Foo.name 对列表进行排序。

问题:如何正确排序这些急切加载的值,使most_recent_bar 合法地是最近创建的,然后根据most_recent_bar.value 排序Foos 列表?

从我的实时代码编辑 SQL:

    Foo.includes(:most_recent_bar).order('bars.value asc, name asc')
  SQL (7.9ms)  SELECT "foos"."id" AS t0_r0, "foos"."name" AS t0_r1, "foos"."created_at" AS t0_r2, "foos"."updated_at" AS t0_r3, "bars"."id" AS t1_r0, "bars"."content" AS t1_r1, "bars"."created_at" AS t1_r2, "bars"."updated_at" AS t1_r3, "bars"."foo_name" AS t1_r4, "bars"."version" AS t1_r5, "bars"."bbj_status" AS t1_r6, "bars"."host" AS t1_r7, "bars"."value" AS t1_r8 FROM "foos" LEFT OUTER JOIN "bars" ON "bars"."foo_name" = "foos"."name" ORDER BY bars.value asc, name asc

在修复后编辑损坏的 SQL 似乎正在寻找两个创建的 ats,我不确定模棱两可的一个来自哪里!

My error SQL Foo.includes(:most_recent_foo).merge(bar.most_recent).order('bars.repo asc')
  SQL (0.6ms)  SELECT "foos"."id" AS t0_r0, "foos"."name" AS t0_r1, "foos"."created_at" AS t0_r2, "foos"."updated_at" AS t0_r3, "bars"."id" AS t1_r0, "bars"."content" AS t1_r1, "bars"."created_at" AS t1_r2, "bars"."updated_at" AS t1_r3, "bars"."foo_name" AS t1_r4, "bars"."version" AS t1_r5, "bars"."bbj_status" AS t1_r6, "bars"."host" AS t1_r7, "bars"."repo" AS t1_r8 FROM "foos" LEFT OUTER JOIN "bars" ON "bars"."foo_name" = "foos"."name" ORDER BY created_at DESC, bars.created_at desc, bars.repo asc
PG::Error: ERROR:  column reference "created_at" is ambiguous
LINE 1: …bars”.”foo_name" = "foos"."name"  ORDER BY created_at...

【问题讨论】:

  • 你能添加这个查询的 SQL 输出吗?
  • 我刚刚做了一个快速搜索和替换,应该可以与 SSCE 一起使用!

标签: ruby-on-rails ruby activerecord ruby-on-rails-4 eager-loading


【解决方案1】:

现在我终于理解了这个问题(我希望如此),但坏消息是:SQL 没有任何简单的命令来按您需要的方式进行排序 - 仅 ORDER BY 是不够的。正因为如此,ActiveRecord 也没有提供简单的解决方案(ActiveRecord 只是一个带有良好/非常好的 SQL 语句生成器的 ORM)。要让任何 ORM 高效地完成,您需要的东西都相当复杂。

好消息是,您可以在完全了解如何使用 SQL 来完成此操作的情况下完成此操作,并通过一些工作来告知 AR 您正在尝试做什么。首先是 SQL。

有多种方法可以按关联行集合的 MAX 进行排序,最有效的方法因 DBMS 和实际数据而异。因为您使用的是 PostgreSQL,所以您可能希望使用窗口函数来完成此操作。问题是使用窗口函数来干净利落地处理预加载将是极其困难的。那么使用 vanilla SQL 还有哪些其他选择呢?一种简单的方法是使用 NOT EXISTS:

SELECT /* some-eager-loaded-fields */
FROM foos
LEFT JOIN bars ON foos.id = bars.foo_id
  AND NOT EXISTS (
    SELECT 1 FROM bars b2
    WHERE b2.foo_id = bars.foo_id AND b2.created_at > bars.created_at
  )
ORDER BY bars.value, foos.name

NOT EXISTS 将排除存在大于created_atfoo_id 的任何条形。这给您留下了唯一没有更大created_at 的条形 - MAX。现在我们有了查询,我们将如何在 AR 中生成它?

子查询如下所示(使用 Arel 帮助正确格式化别名):

b2 = Bar.arel_table.alias('b2')
Bar.select('1').from(b2).where('b2.foo_id = bars.foo_id AND b2.created_at > bars.created_at')

这现在可以用来确定最近的:

def Bar < AR::Base
  def self.most_recent
    b2 = arel_table.alias('b2')
    where Bar.select('1').from(b2).
      where('b2.foo_id = bars.foo_id AND b2.created_at > bars.created_at').
      exists.not
  end
end

还有你的most_recent_bar 关联:

def Foo < AR::Base
  has_many :bars
  has_one :most_recent_bar, ->{ merge(Bar.most_recent) }, class_name: 'Bar'
end

现在您的查询应该可以正常工作了:

Foo.includes(:most_recent_bar).order('bars.value asc, foos.name asc')

【讨论】:

  • 在重构之前,它似乎可以工作,但它列出的不仅仅是一个 most_recent_bar。现在我根本无法让它工作。将继续搞砸这个。只是出于好奇(实际上昨天看到了这样的东西)为什么 most_recent_foo ?我会研究 Bill Karwin 的建议。感谢您的帮助!
  • 抱歉,这与most_recent_bar 混淆了。固定。
  • @user1870954 - 更改most_recent类方法实现以指定表名:order('bars.created_at desc')
  • 我撒谎了,这实际上并不能满足我的需要。它在 Bar 创建时对主列表进行排序。扑通!
  • @user1870954 - 我很困惑,这不是您想要的排序顺序吗? “根据 most_recent_bar.value 对 Foos 列表进行排序”。请说明主列表应如何排序。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-31
  • 2016-04-25
相关资源
最近更新 更多