【问题标题】:Rails scope complexityRails 范围复杂性
【发布时间】:2014-10-10 10:45:12
【问题描述】:

我有一个需要为其创建默认范围的模型。我不确定编写此范围的最佳方式,但我会解释它需要如何工作。

基本上我需要获取模型的所有项目,如果两个项目具有相同的“订单”值,那么它应该查看“版本”字段(将包含 1、2、3 等)并选择一个价值最高。

有没有办法只用一个范围来实现这一点?

【问题讨论】:

  • 这种情况下的顺序是什么?
  • 订单只是一个普通整数

标签: ruby-on-rails activerecord scope


【解决方案1】:

在 sql 中,您可以轻松地对两件事进行排序,如果第一件事相等,则首先对第一件事排序,然后对第二件事排序。所以在你的情况下,就像

select * from posts order by order_field_1, version desc

你不能命名一个列order,因为它是一个sql保留字,而且你没有给出真正的列名,我只是把它命名为order_field_1

这很容易翻译成rails:

Post.order(:order_field_1, version: :desc)

我通常建议不要使用default_scope,因为一旦设置它就真的很难避免(它被预先添加总是),但如果你真的需要它并且知道风险,它真的是也适用:

class Post < ActiveRecord::Base

  default_scope { order(:order_field_1, version: :desc) }

end

这实际上在rails guides 中有很好的记录。

【讨论】:

    【解决方案2】:

    首先,default_scopes 很危险。每当您使用模型时,它们都会被使用,除非您特别强制“无范围”。 IME,很少需要对模型的每次使用都有一个范围。并非不可能,但很少见。当你有这么大的计算量时,这种情况就更罕见了。

    您可以简化问题,而不是进行复杂的查询吗?这是一种方法:

    为了使版本字段起作用,您可能有一些代码已经在比较订单字段(否则您不会有两个订单字段相同但版本字段不同的唯一行)。因此,您可以创建一个新字段,该字段的值高于指示要返回的正确实体的最后一个字段。也就是说,为了创建一个新的唯一版本,您知道您最后有一个最重要的行。采用最重要行的排序顺序,并加一。这是新的最重要行的排序顺序。

    现在您可以查询具有最高排序顺序的合格数据 (order_by(sort_order, 'DESC').first)。

    与其关注查询,不如关注你是否存储了正确的数据,即能不能实现你想要的查询,更简单。在这种情况下,您似乎已经在进行一项有助于确定胜诉案例的操作。所以使用该代码和现有的数据库操作,以减少未来的数据库操作。

    【讨论】:

      【解决方案3】:

      ActiveRecord order 方法只使用可以有多个参数的 SQL ORDER 函数。假设您有一些具有orderversion 属性的模型,那么按照您的描述对记录进行排序的正确方法是order(:order, :version)。如果您希望将此作为默认范围,您最终会得到:

      default_scope { order(:order, :version) }
      

      【讨论】:

        【解决方案4】:

        使用带有顺序的默认范围时要注意。当您对集合(例如update_all)执行更新时,它将使用默认范围来获取记录,并且您认为快速操作将使您的数据库崩溃,因为它在更新之前将行复制到临时表.

        我建议只使用普通范围而不是默认范围。

        查看Select the 3 most recent records where the values of one column are distinct,了解如何构造您想要的sql查询,然后将其放入How to chain or combine scopes with subqueries or find_by_sql中提到的find_by_sql statemate

        【讨论】:

        • 感谢您的评论。我之前确实研究过 SQL 中的分组,但它确实让我对它的作用感到困惑。我尝试了你之前的代码 sn-p 并得到:ActiveRecord::StatementInvalid: PG::GroupingError: ERROR: column "coasters.id" must appear in the GROUP BY clause or be used in an aggregate function 顺便说一句,我正在使用 PostgreSQL。
        • 刚刚尝试在 SQL 中直接返回正确的结果。我有这个查询:“SELECT order_ridden, max(version) AS version FROM Coasters GROUP BY order_ridden” 这将返回正确数量的结果,但我需要所有其他字段。每当我尝试添加它们时,它都会说它们需要包含在 group by 子句中,而当我这样做时,我会得到错误的结果。有任何想法吗?一旦 Ia hev 它在普通 SQL 中工作,我就可以继续将其转换为 ActiveRecord 并将其用作正常范围。
        • 对此还有什么想法吗?我只需要它添加其余字段,但记录中与 max(version) 记录匹配的字段。我急于解决这个问题。
        【解决方案5】:

        试试这个代码:

        scope :group_by_order, -> { order('order ASC').group('order') }
        default_scope, { (group_by_order.map{ |key,values| values.order('version DESC') }.map{|key, values| values - values[1..-1]}).values.flatten }
        

        解释代码:

        1. 按“订单”字段排序。
        2. 按“订单”字段分组。
        3. 映射结果哈希,并按“版本”字段对每个值进行排序
        4. 再次映射值,并从索引“1”删除到末尾。
        5. 获取所有值并将它们展平

        【讨论】:

        • 谢谢。很快就会试试这个。您是否有机会添加并解释这实际上是如何工作的?
        • 尝试此操作时,我得到“意外”}。
        • 嗯,在 default_scope 行上仍然出现意外 }
        • 其实我用了sublime,找不到不必要的“}”?,你能帮我找到这个括号吗:),或者使用你范围的解释代码步骤:)
        • 如果使用默认范围,则将其用作 default_scope { (group_by_order.map{ |key,values| values.order('version DES') }.map{|key, values| values -值[1..-1]}).values.flatten }。使用命名作用域时,应在作用域名称后使用-> (lambda) 符号
        猜你喜欢
        • 2011-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-28
        • 1970-01-01
        • 1970-01-01
        • 2015-05-05
        相关资源
        最近更新 更多