使用带有活动记录的连接表时如何连接模型的多个实例?

Posted

技术标签:

【中文标题】使用带有活动记录的连接表时如何连接模型的多个实例?【英文标题】:How to join with multiple instances of a model when using a join table with active record? 【发布时间】:2020-09-01 19:16:24 【问题描述】:

这适用于使用 PostgreSQL 的 Rails 5 / Active Record 5。

假设我有两个模型:ProductWidget。一个 Product has_many 小部件和一个 Widget has_many 产品通过名为 products_widgets 的连接表。

我想编写一个查询来查找与 两个id=37 的小部件和 与 id=42 的小部件相关联的所有产品。

我其实有一个id列表,但是如果我能写出上面的查询,我一般可以解决这个问题。

请注意,此查询的更简单版本是查找与 要么 具有 id=37 的小部件 具有 id=42 的小部件相关联的所有小部件,这你可以这样写:

Product.joins(:products_widgets).where(products_widgets: widget_id: [37, 42])

但这不是我需要的。

【问题讨论】:

【参考方案1】:

作为初学者:在纯 SQL 中,您可以使用 exists 条件来表达查询:

select p.*
from product p
where 
    exists (
        select 1 
        from products_widgets pw 
        where pw.product_id = p.product_id and pw.widget_id = 37
    )
    and exists (
        select 1 
        from products_widgets pw 
        where pw.product_id = p.product_id and pw.widget_id = 42
    )

在活动记录中,我们可以尝试直接在where条件下使用原始子查询:

product
    .where('exists(select 1 from products_widgets where product_id = product.product_id and widget_id = ?)', 37)
    .where('exists(select 1 from products_widgets where product_id = product.product_id and widget_id = ?)', 42)

我认为使用.arel.exist 也可能有效:

product
    .where(products_widgets.where('product_id = product.product_id and widget_id = ?', 37).arel.exists)
    .where(products_widgets.where('product_id = product.product_id and widget_id = ?', 42).arel.exists)

【讨论】:

这真的很有帮助——只是一个旁注,这种策略的索引要求是什么?对于该确切查询,我是否需要“存在”索引,或者我在 id 列上的现有索引是否足够? @SamJohnson:对于这个查询,我建议在products_widgets(product_id, widget_id) 上建立一个索引,这样exists 子查询可以快速执行(但也许它已经到位)。

以上是关于使用带有活动记录的连接表时如何连接模型的多个实例?的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4 - 如何在活动记录查询中为包含()和连接()提供别名

如何在连接多个表时使用 GROUP BY 连接字符串?

连接多个大表时如何进行更快的查询?

如何在连接多个必须是 PIVOT 的表时创建临时表

如何对来自多个模型的记录进行分页? (我需要多态连接吗?)

使用 Spark SQL 创建带有连接的 Hive 表时使用的引擎