产品属性表上的交集查询

时间:2016-04-21 13:27:30

标签: mysql sql

以下是我的电子商务模式的简化版本:

[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”属性的产品。

如何才能获得具有这两个属性的产品?

2 个答案:

答案 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甚至支持包含其他列,例如这种情况。