【问题标题】:Sort by specific ids in ActiveRecord按 ActiveRecord 中的特定 id 排序
【发布时间】:2013-09-24 20:50:54
【问题描述】:

我继承了另一个程序员的 Rails3 项目,而且我对 Rails 总体来说还很陌生。他有一个查询,似乎按特定 ID 排序。有人可以解释这在实际 SQL 中是如何解决的吗?我认为这段代码正在杀死 db 并随后杀死 rails。我试图在记录器中输出它,但即使配置设置为:debug,似乎也无法输出实际的 SQL。在这里(在 SO 上)进行大量搜索并没有清楚地解释这个查询的外观。代码如下:

options = {
              select: "SUM(1) AS num_demos, product_id ",
              group:  "product_id",                
              order:  "num_demos ASC",
            }
  product_ids = Demo.where("state = 'waitlisted'").find(:all, options).collect{|d| d.product_id}
  sort_product_ids = product_ids.collect{|product_id| "id = #{product_id}"}   
  Product.where(visible: true, id: product_ids).order(sort_product_ids.join(', '))   

据我所见,最后一行将使用 ORDER BY "id = 1, id = 3, ..." 等创建针对产品表的查询,这没有多大意义我。感谢所有线索。

【问题讨论】:

  • 在查询结束时使用to_sql 并检查它以查看生成的SQL
  • 谢谢,从 rails 控制台得到了相同的输出。它看起来像这样:SELECT "products".* FROM "products" WHERE "products"."visible" = 't' AND "products"."id" IN (166, 105,..etc) ORDER BY id = 166 , id = 105, id = 110, id = 99, id = 109, id = 118, id = 108, id = 160,... 等等。我以前没见过这种类型的 ORDER BY,而且很酷.

标签: ruby-on-rails ruby-on-rails-3 activerecord


【解决方案1】:

快速细分正在发生的事情,因为它将帮助您了解如何处理替换查询。

options = {
              select: "SUM(1) AS num_demos, product_id ",
              group:  "product_id",                
              order:  "num_demos ASC",
            }
product_ids = Demo.where("state = 'waitlisted'").find(:all, options).collect{|d| d.product_id}

这一行会生成

SELECT SUM(1) as num_demos, product_id FROM "demos" WHERE (state = 'waitlisted') GROUP BY product_id

并返回Demo 对象数组,按组中行的count(*) 排序,其中仅加载了product_id 属性,可供您使用。

接下来,

sort_product_ids = product_ids.collect{|product_id| "id = #{product_id}"}   

产生映射为"id = x" 格式的product_id 集合。 IE:如果之前的结果返回10个结果,product_ids的范围是1..10,sort_product_ids现在相当于["id = 1", "id = 2", "id = 3", "id = 4", "id = 5", "id = 6", "id = 7", "id = 8", "id = 9", "id = 10"]

最后,

Product.where(visible: true, id: product_ids).order(sort_product_ids.join(', '))

选择所有Products,其中visible 列是true,并且它们的idproduct_ids 数组中(正如我们之前发现的,它实际上是Demo 对象数组,而不是整数 - 这可能会导致查询失败)。然后,它要求 SQL 按sort_product_ids 对结果列表进行排序(作为字符串"id = 1, id = 2, ... id = 10" 而不是数组["id = 1", "id = 2", ... "id = 10"] 发送)。

更多信息请访问: http://guides.rubyonrails.org/active_record_querying.html http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html

【讨论】:

  • 感谢您的分析。在我在 irb 的测试中,数组 sort_product_ids 将包含“id = 1”、“id = 2”等条目。当您调用 join(', ') 时,您应该得到一个包含所有这些条目的单个字符串: "id = 1, id = 2" 等等。ActiveRecord 实际上创建的 SQL 是否显示... ORDER BY ('id = 1, id = 2, ...') ?
  • 非常感谢,@Morner。想投票给这个答案,但我没有足够的声誉......
  • 没问题,你的问题解决了吗?如果是,请选择我的答案
  • 差不多。我终于让 rails 控制台工作了(必须为 DATABASE_URL 和 RAILS_ENV 正确设置环境变量,对于任何正在努力做到这一点的人来说),然后在控制台中运行上面的代码。它和你说的差不多,但有趣的是,记录是从 sort_product_ids 上的连接创建的列表中以相反的顺序返回的。为什么会这样?
  • 不确定,但您可以通过将"ASC""DESC" 之一附加到sort_product_ids.join(', ') 字符串来反转它;以相反的排序为准。
【解决方案2】:

要按给定的 id 数组进行选择和排序,您可以使用这个

Product.where(visible: true, id: product_ids)
.order( "field(id,#{product_ids.join(',')})" )

【讨论】:

  • 你的意思是,它只会选择给定 id 的记录,并按照 product_ids.join(',') 提供的 id 顺序返回它们吗?
  • 它将选择具有给定 id 和 visible = true 的记录。然后它将按给定的顺序排列它们。如果product 不存在于最终结果中,它将忽略其顺序中的id
  • 这正是我想要的。它通过一组给定的 id 对我的模型进行排序。
  • @tihom 这有帮助,但我得到了:(PG::SyntaxError: ERROR: syntax error at or near "[" LINE 1: ...,2,3,4,9532,9533,88,89)) ORDER BY FIELD(stage_id, [1, 2, 3, ...
  • @sscirrus order by field 功能在 PG 中不可用,请参阅 stackoverflow.com/questions/1309624/…
【解决方案3】:

如果您使用的是 PostgreSQL,请考虑使用WITH ORDINALITY,与其他方式相比,这是最快的方式。见this thread

将此方法应用于 Ruby on Rails,例如:

class SpecifiedByIds
  def specified_by_ids(ids)
    joins(
      <<~SQL
        LEFT JOIN unnest(array[#{ids.join(',')}])
        WITH ORDINALITY AS t(id,odr) ON t.id = #{table_name}.id
      SQL
    ).order('t.odr')
  end
end

class MyModel < ActiveRecord::Base
  extend SpecifiedByIds
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-09
    • 2012-06-29
    • 1970-01-01
    • 2014-12-06
    相关资源
    最近更新 更多