使用带有活动记录的连接表时如何连接模型的多个实例?
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。
假设我有两个模型:Product
和 Widget
。一个 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
子查询可以快速执行(但也许它已经到位)。以上是关于使用带有活动记录的连接表时如何连接模型的多个实例?的主要内容,如果未能解决你的问题,请参考以下文章