SQL:按属性列表过滤产品

时间:2016-02-22 20:40:44

标签: mysql sql join filtering union

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

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

  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]
  5. 选择所有匹配的产品

    此方法需要N个过滤器的N + 1个数据库查询。我希望可以在一个查询中将它结合起来。

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

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

    如何通过单个数据库查询(或尽可能少)来实现这一目标?

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

    谢谢!

1 个答案:

答案 0 :(得分:3)

这就是我试图解决的问题:

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'。
  • 其次,执行将过滤器与产品匹配的查询。这就是SELECTJOIN正在做的事情。
  • 第三,对产品ID执行GROUP BY,并对结果行执行COUNT()。在HAVING子句中,检查产品计数是否与过滤器总数相同。如果是,那么它必须匹配所有这些。

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