如何在 Rails 中对 AREL 中的子查询进行连接

Posted

技术标签:

【中文标题】如何在 Rails 中对 AREL 中的子查询进行连接【英文标题】:How to do joins on subqueries in AREL within Rails 【发布时间】:2011-11-23 15:10:33 【问题描述】:

我有一个简单的模型

class User
    has_many :logs


class Logs

通过外键logs.user_id以通常的方式关联。我正在尝试使用 Arel 执行以下操作,根据 Arel 文档,它应该可以工作。

u_t = Arel::Table::new :users
l_t = Arel::Table::new :logs

counts = l_t.
    group(l_t[:user_id]).
    project(
        l_t[:user_id].as("user_id"),
        l_t[:user_id].count.as("count_all")
    )

l_t.joins(counts).on(l_t[:id].eq(counts[:user_id]))

当我这样做时,我得到了错误

TypeError: Cannot visit Arel::SelectManager

但是Arel的作者explicitly suggests认为Arel可以做这种事情。

请不要写关于如何使用原始 sql、另一种类型的 Arel 查询等实现相同查询的回复。这是我感兴趣的模式,而不是该查询的具体结果。

【问题讨论】:

【参考方案1】:

这实现了与 Arel 的嵌套选择子查询的连接:

您可以在模型文件中添加嵌套的 inner_query 和 outer_query 范围并使用 ...

  inner_query = Model.inner_query(params)
  result = Model.outer_query(params).joins(Arel.sql("(#inner_query.to_sql)"))
   .group("...")
   .order("...")

对于这方面的变体,例如在子查询上使用 INNER JOIN,请执行以下操作:

  inner_query = Model.inner_query(params)
  result = Model.outer_query(params).joins(Arel.sql("INNER JOIN (#inner_query.to_sql) tablealias ON a.id = b.id"))
   .group("...")
   .order("...")

在每个查询的范围内添加特定的连接、约束和分组,以进一步修改 sql 语句,即:

scope :inner_query , -> (company_id, name) 
    select("...")
    .joins("left join table1 on table1.id = table2.id")
    .where("table1.company_id = ? and table1.name in (?)", company_id, name)
    .group("...")

这允许您在嵌套查询和外部查询上放置 WHERE 条件

【讨论】:

【参考方案2】:

您可以使用 join_sources 从 Arel::SelectManager 的实例中检索 Arel::Nodes::Join,并将其传递给 joins

使用您的示例:

l_t.joins(counts.join_sources).on(l_t[:id].eq(counts[:user_id]))

【讨论】:

l_t[:id].eq(counts[:user_id]) 这行得通吗?我的意思是,为什么日志的 id 应该等于用户的 id?

以上是关于如何在 Rails 中对 AREL 中的子查询进行连接的主要内容,如果未能解决你的问题,请参考以下文章

如何使用ARel对子查询进行连接?

如何使用 Arel(大概)创建一个不影响 Rails 3 中的查询的 ActiveRecord 范围?

Rails 3.0 中的 Arel 到底是啥?

Arel、联接和 Rails 查询

Arel 中的嵌套查询

Rails:将复杂的 SQL 查询转换为 Arel 或 ActiveRecord