【问题标题】:SQL: Filter products by a list of propertiesSQL:按属性列表过滤产品
【发布时间】:2016-06-04 10:54:16
【问题描述】:

我有一张名为products 的表。 (id, name, price),一个名为properties (id, name, type) 的表和一个名为product_properties (id, property_id, product_id,值)。

现在,我可以按如下方式过滤产品:

  1. product_ids 列表开始。
  2. 对于我们要过滤的每个(属性、值):
    1. product_ids = SELECT 'product_properties'.'product_id' FROM 'product_properties' WHERE 'product_properties'.'product_id' IN [list,of,product,ids] AND 'product_properties'.'property_id' = property_id AND 'product_properties'.'value' = 'some value here'
  3. 重复第 2 步,直到我们过滤掉所有想要的属性。
  4. 最后,使用SELECT * FROM 'products' WHERE 'products'.'id' IN [list,of,product,ids]选择所有匹配的产品

此方法需要对 N 个过滤器进行 N+1 个数据库查询。我希望可以将其组合到一个查询中。

我尝试了以下方法,但不起作用:

  • INNER JOIN/OUTER JOIN -> 放弃指向除第一行之外的所有产品行的所有 product_properties,然后尝试对其强制执行所有 N 个 ID 和值。
  • UNION -> 这将返回一个列表,其中包含与 一个或多个 过滤器匹配的产品,而不是 所有 个过滤器。

如何通过单个数据库查询(或尽可能少)来完成?

我正在使用 Ruby on Rails,这意味着我希望结果在 MySQL 和 SQLite 中都可以使用。

谢谢!

【问题讨论】:

    标签: mysql sql join filtering union


    【解决方案1】:

    这就是我尝试解决的方法:

    WITH FILTERS (property_id, filter_value) AS
    (
              SELECT 1, 'foo'
    UNION ALL SELECT 2, 'bar'
    UNION ALL SELECT 3, 'baz'
    )
    SELECT prods.id
      FROM products prods
      JOIN product_properties props
        ON ( props.product_id = prods.id )
      JOIN filters f
        ON ( f.property_id = props.property_id AND f.filter_value = props.value )
     GROUP BY prods.id
    HAVING COUNT(1) = ( SELECT COUNT(1) FROM filters )
    

    这是在做什么:

    • 首先,您提供过滤器列表作为“WITH”子句的一部分,以便它可以在同一个查询中。我假设在 3 个不同的属性上添加 3 个过滤器,这些属性的值分别为“foo”、“bar”和“baz”。
    • 其次,执行将过滤器与产品匹配的查询。这就是SELECTJOINs 正在做的事情。
    • 第三,对产品 ID 执行GROUP BY,并在结果行上执行COUNT()。在HAVING 子句中,检查产品计数是否与过滤器总数相同。如果是,那么它必须匹配所有这些。

    注意:您可以交替使用UNION 方法,然后对结果执行GROUP BYHAVING COUNT() 以查看它是否有效。请确保您对 ID 执行 UNION ALL 而不仅仅是 UNION

    【讨论】:

    • OP 请求了一个 MySQL 和 SQLite 兼容的解决方案。虽然在 2014 年初,SQLite 3.8.3 提供 CTE,但 MySQL 不提供,所以 With() 将无法工作。
    • 哇! UNION ALL ... GROUP BY ... HAVING COUNT() 方法完美运行。一个非常好的技巧。非常感谢!
    猜你喜欢
    • 2011-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-13
    • 1970-01-01
    相关资源
    最近更新 更多