Find_by_sql 作为 Rails 范围

Posted

技术标签:

【中文标题】Find_by_sql 作为 Rails 范围【英文标题】:Find_by_sql as a Rails scope 【发布时间】:2014-10-16 18:15:57 【问题描述】:

来自 Sitepoint 的 r937 非常友好地帮助我找出从数据库返回正确结果所需的查询。

我需要的是能够将此查询用作范围,并能够将其他范围链接到这个范围。

查询是:

SELECT coasters.*
FROM (
    SELECT order_ridden,
           MAX(version) AS max_version
    FROM coasters
    GROUP BY order_ridden
) AS m
INNER JOIN coasters
ON coasters.order_ridden = m.order_ridden
AND COALESCE(coasters.version,0) = COALESCE(m.max_version,0)

我尝试制作这样的范围:

  scope :uniques, lambda 
    find_by_sql('SELECT coasters.*
                 FROM (
                   SELECT order_ridden,
                          MAX(version) AS max_version
                   FROM coasters
                   GROUP BY order_ridden
                 ) AS m
                 INNER JOIN coasters
                 ON coasters.order_ridden = m.order_ridden
                 AND COALESCE(coasters.version,0) = COALESCE(m.max_version,0)')
  

但是当我尝试将另一个示波器链接到它时,它失败了。有没有办法像普通范围一样运行此查询?

【问题讨论】:

【参考方案1】:

find_by_sql 返回一个Array。但是您需要一个 ActiveRecord::Relation 来链接其他范围。

使用返回 ActiveRecord::Relation 的 ActiveRecord 方法重写查询的一种方法是稍微重新排列它,以便嵌套在 INNER JOIN 部分中发生。

您可能想尝试以下方法:

scope :uniques, lambda 
  max_rows = select("order_ridden, MAX(version) AS max_version").group(:order_ridden)
  joins("INNER JOIN (#max_rows.to_sql) AS m
    ON coasters.order_ridden = m.order_ridden
   AND COALESCE(coasters.version,0) = COALESCE(m.max_version,0)")

【讨论】:

嘿@cschroed 效果很好!谢谢。对于这实际上是如何工作的,我仍然有点困惑。我意识到我需要一个 ActiveRecord::Relation,但是如何重新排列你输出的 AR::Relation? 很高兴它成功了!我们以一种可以使用称为joinsActiveRecord 查询方法而不是find_by_sql 的方式编写此代码。请参阅其他方法listed here。它们都返回一个ActiveRecord::Relation 并且可以被链接。如果使用Coaster.uniques.class,可以在Rails控制台查看返回值的类。

以上是关于Find_by_sql 作为 Rails 范围的主要内容,如果未能解决你的问题,请参考以下文章

Rails Activerecord 关系:使用子查询作为 SQL 选择语句的表

ARel 模仿包含 find_by_sql

Rails 5范围问题

Rails 中无限时间范围的问题

Rails 包含范围

rails 3.0中的多个范围