【问题标题】: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]})

但这不是我需要的。

【问题讨论】:

    标签: ruby-on-rails postgresql activerecord subquery inner-join


    【解决方案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 子查询可以快速执行(但也许它已经到位)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 2017-09-17
    • 1970-01-01
    • 1970-01-01
    • 2013-07-30
    • 2013-08-02
    • 1970-01-01
    相关资源
    最近更新 更多