以下是我的电子商务模式的简化版本:
[products]:
id
Name
Price
URL
[attributes]:
id
Description
Value
[products_attributes]:
products_id
attributes_id
这是典型的多对多关系。
现在我需要查询所有'Red'和'XXL'产品。
SELECT p.*
FROM products p
INNER JOIN products_attributes pa ON pa.products_id = p.id
INNER JOIN attributes pa ON pa.attributes_id = a.id
WHERE a.Value IN ('Red','XXL');
显然,SQL查询会获取所有“红色”产品,即使是那些没有“XXL”属性的产品。
如何才能获得具有这两个属性的产品?
答案 0 :(得分:3)
您需要单独检查每个,这是EAV模型通常设计不良的众多原因之一。您可以通过为EXISTS
执行两个子查询来完成此操作:
SELECT
P.id, P.name, P.price, P.url -- Because we never use SELECT * except for EXISTS, COUNT(*), etc.
FROM
Products P
WHERE
EXISTS
(
SELECT *
FROM
Products_Attributes PA_RED
INNER JOIN Attributes A_RED ON
A_RED.attribute_id = PA_RED.attribute_id AND
A_RED.description = 'Color' AND -- Have you thought about the possibility that "Red" might be a value for multiple attributes?
A_RED.value = 'Red'
WHERE
PA_RED.product_id = P.product_id
) AND
EXISTS
(
SELECT *
FROM
Products_Attributes PA_XXL
INNER JOIN Attributes A_XXL ON
A_XXL.attribute_id = PA_XXL.attribute_id AND
A_XXL.description = 'Size' AND
A_XXL.value = 'XXL'
WHERE
PA_XXL.product_id = P.product_id
)
您还可以JOIN
对每个表格进行两次:
SELECT
P.id, P.name, P.price, P.url
FROM
Products P
INNER JOIN Products_Attributes PA_RED ON PA_RED.product_id = P.product_id
INNER JOIN Attributes A_RED ON
A_RED.attribute_id = PA_RED.attribute_id AND
A_RED.description = 'Color' AND
A_RED.value = 'Red'
INNER JOIN Products_Attributes PA_XXL ON PA_XXL.product_id = P.product_id
INNER JOIN Attributes A_XXL ON
A_XXL.attribute_id = PA_XXL.attribute_id AND
A_XXL.description = 'Size' AND
A_XXL.value = 'XXL'
当然,想想检查5个不同的属性时,请考虑一下该查询的内容......
另一种方法是将匹配计数作为聚合来检查。您需要确保相同的属性不能与同一产品匹配两次,但是:
SELECT
P.id, P.name, P.price, P.url
FROM
Products P
INNER JOIN Products_Attributes PA ON PA.product_id = P.product_id
INNER JOIN Attributes A ON
A.attribute_id = PA.attribute_id AND
(A.description = 'Color' AND A.value = 'Red') OR
(A.description = 'Size' AND A.value = 'XXL')
GROUP BY
P.id, P.name, P.price, P.url
HAVING
COUNT(*) = 2
答案 1 :(得分:1)
一种方法是使用两个连接。但是,我更喜欢聚合和group by
:
SELECT p.*
FROM products p INNER JOIN
products_attributes pa
ON pa.products_id = p.id INNER JOIN
attributes pa
ON pa.attributes_id = a.id
WHERE a.Value IN ('Red','XXL')
GROUP BY p.id
HAVING COUNT(DISTINCT a.Value) = 2;
注意:这是在聚合查询中选择p.*
。总的来说,我强烈反对这样做。但是,在这种情况下,聚合是通过products
表上的主键进行的,因此从该表中选择其他列是安全的。事实上,ANSI SQL甚至支持包含其他列,例如这种情况。