SQL:按属性列表过滤产品
SQL: Filter products by a list of properties
我有一个叫 products
的 table。 (id, name, price),一个叫做 properties
(id, name, type) 的 table 和一个 table 称为 product_properties
(id, property_id, product_id, value).
现在,我可以按如下方式筛选产品:
- 从 product_ids.
列表开始
- 对于我们要过滤的每个(属性,值):
- 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'
- 重复第 2 步,直到我们过滤完所有需要的属性。
- 最后,select 所有使用
SELECT * FROM 'products' WHERE 'products'.'id' IN [list,of,product,ids]
的匹配产品
此方法需要针对 N 个过滤器进行 N+1 次数据库查询。我希望可以将其合并到一个查询中。
我尝试了以下方法,但不起作用:
- INNER JOIN/OUTER JOIN -> 丢弃所有 product_properties 指向除第一行以外的产品行,然后尝试强制执行所有 N ID 和值在上面。
- UNION -> 此 returns 列表包含匹配 一个或多个 过滤器的产品,而不是 所有 过滤器。
如何通过单个数据库查询(或尽可能少的查询)完成此操作?
我在 Rails 上使用 Ruby,这意味着我希望结果在 MySQL 和 SQLite 中都能工作。
谢谢!
这就是我尝试解决它的方法:
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' 子句的一部分,以便它可以在同一个查询中。我假设在具有值 'foo'、'bar' 和 'baz'.
的 3 个不同属性上添加 3 个过滤器
- 其次,执行将过滤器与产品相匹配的查询。这就是
SELECT
到 JOIN
正在做的事情。
- 第三,对产品 ID 执行
GROUP BY
,并对结果行执行 COUNT()
。在 HAVING
子句中,检查产品计数是否与过滤器总数相同。如果是,那肯定是全部匹配了。
注意:您可以交替使用 UNION
方法,然后对结果执行 GROUP BY
和 HAVING COUNT()
以查看其是否有效。只需确保对 ID 执行 UNION ALL
而不仅仅是 UNION
.
我有一个叫 products
的 table。 (id, name, price),一个叫做 properties
(id, name, type) 的 table 和一个 table 称为 product_properties
(id, property_id, product_id, value).
现在,我可以按如下方式筛选产品:
- 从 product_ids. 列表开始
- 对于我们要过滤的每个(属性,值):
- 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'
- product_ids =
- 重复第 2 步,直到我们过滤完所有需要的属性。
- 最后,select 所有使用
SELECT * FROM 'products' WHERE 'products'.'id' IN [list,of,product,ids]
的匹配产品
此方法需要针对 N 个过滤器进行 N+1 次数据库查询。我希望可以将其合并到一个查询中。
我尝试了以下方法,但不起作用:
- INNER JOIN/OUTER JOIN -> 丢弃所有 product_properties 指向除第一行以外的产品行,然后尝试强制执行所有 N ID 和值在上面。
- UNION -> 此 returns 列表包含匹配 一个或多个 过滤器的产品,而不是 所有 过滤器。
如何通过单个数据库查询(或尽可能少的查询)完成此操作?
我在 Rails 上使用 Ruby,这意味着我希望结果在 MySQL 和 SQLite 中都能工作。
谢谢!
这就是我尝试解决它的方法:
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' 子句的一部分,以便它可以在同一个查询中。我假设在具有值 'foo'、'bar' 和 'baz'. 的 3 个不同属性上添加 3 个过滤器
- 其次,执行将过滤器与产品相匹配的查询。这就是
SELECT
到JOIN
正在做的事情。 - 第三,对产品 ID 执行
GROUP BY
,并对结果行执行COUNT()
。在HAVING
子句中,检查产品计数是否与过滤器总数相同。如果是,那肯定是全部匹配了。
注意:您可以交替使用 UNION
方法,然后对结果执行 GROUP BY
和 HAVING COUNT()
以查看其是否有效。只需确保对 ID 执行 UNION ALL
而不仅仅是 UNION
.