如何使此eav查询生成水平结果

时间:2013-02-06 23:18:04

标签: mysql sql entity-attribute-value

案例:

表:

product:
product_id|name        |
------------------------
1         |iphone 4    |
2         |gallaxy 2   |
3         |blackbery 6 |

product_attribute:

id|product_id|attribute_id
--------------------------------------------------
 1 |1        |2
 2 |1        |6
 .    .        .

attribute:
------------------------------
attribute_id|name  |value|
        1   |width |300
        2   |width |320
        3   |width |310
        4   |height|390
        5   |height|370
        6   |height|380

应该得到结果:

product_id|height|width
 1        |380   |320
 ......................

修改 高度和宽度属性只是产品属性的一部分 - 产品需要具有动态能力,由用户在后端添加,就像在magento中一样,因为我选择了eav db design。 如果我们不知道产品的名称,请写下查询。

由于

3 个答案:

答案 0 :(得分:5)

有几种方法可以实现这一点。对于每个属性值,这样的事情应该多次连接在表上:

SELECT p.product_id,
    a.value height,
    a2.value width
FROM Product p
    JOIN Product_Attribute pa ON p.product_id = pa.product_id 
    JOIN Attribute a ON pa.attribute_id = a.attribute_id AND a.name = 'height'
    JOIN Product_Attribute pa2 ON p.product_id = pa2.product_id 
    JOIN Attribute a2 ON pa2.attribute_id = a2.attribute_id AND a2.name = 'width'

这是Fiddle

以下是使用MAX和GROUP BY的替代方法,我个人更喜欢:

SELECT p.product_id,
    MAX(Case WHEN a.name = 'height' THEN a.value END) height,
    MAX(Case WHEN a.name = 'width' THEN a.value END) width
FROM Product p
    JOIN Product_Attribute pa ON p.product_id = pa.product_id 
    JOIN Attribute a ON pa.attribute_id = a.attribute_id 
GROUP BY p.product_id
祝你好运。

答案 1 :(得分:3)

一种方法是在SELECT列表中使用相关子查询,尽管这可能不是大型集上性能的最佳值。要从产品表中检索几行行,它不会很糟糕。 (你肯定想要合适的索引。)

SELECT p.product_id
     , ( SELECT a1.value
           FROM attribute a1
           JOIN product_attribute q1
             ON q1.attribute_id = a1.attribute_id
          WHERE q1.product_id = p.product_id
            AND a1.attribute_name = 'height'
          ORDER BY a1.id
          LIMIT 0,1
       ) AS height_1
     , ( SELECT a2.value
           FROM attribute a2
           JOIN product_attribute q2
             ON q2.attribute_id = a2.attribute_id
          WHERE q2.product_id = p.product_id
            AND a2.attribute_name = 'width'
          ORDER BY a2.id
          LIMIT 0,1
       ) AS width_1
  FROM product p
 WHERE p.product_id = 1

此查询将返回product中的行以及属性的值(如果存在)。如果未找到属性值,则查询将返回NULL以代替属性值。 (这与使用INNER JOIN代替相关子查询的查询的行为不同...其中属性或product_attribute表中的“缺失”行将过滤掉返回的产品中的行。)

LIMIT子句的目的是保证子查询返回不返回多行。 (如果SELECT列表中的子查询返回多行,MySQL将返回错误。)ORDER BY的目的是使查询具有确定性,如果有多行满足子查询的话。 (如果没有ORDER BY子句,当有多行时,MySQL可以随意返回它选择的行。)

对于“多值”属性,同样的方法也适用。我们只需添加更多子查询,但指定LIMIT 1,1以返回第二个属性值,LIMIT 2,1以返回第三个值,等等。


(哦,关系数据库中实现的EAV模型的乐趣。)


跟进:

问:“...更常见的情况,因为它发生在我们之前不知道属性名称的eav db中。”

A:关系模型基于元组包含指定类型的指定数量的列的原则。

您(显然)尝试做的是在运行查询时返回可变数量的列。 SELECT语句包括要返回的特定表达式列表;这不能改变,每个表达式返回的值的数据类型不会因行而异。

上面的查询为每个产品返回一个“height”属性值的实例,以及一个“width”属性值的实例。

对于更“更一般的情况”,我们真的希望每个属性值都在它自己的单独行上返回。

更一般的查询,如果您不“提前”知道与产品相关联的属性将是:

SELECT p.product_id
     , a.attribute_id
     , a.name         AS attribute_name
     , a.value        AS attribute_value
  FROM product p
  LEFT
  JOIN product_attribute q
    ON q.product_id = p.product_id
  LEFT
  JOIN attribute a
    ON a.attribute_id = q.attribute_id
 WHERE p.product_id = 1
 ORDER
    BY p.product_id
     , a.name
     , a.attribute_id

这将返回一个可以轻松处理的结果集:

product_id attribute_id attribute_name attribute_value
---------- ------------ -------------- ---------------
         1            6 height         380
         1            2 width          320

问:“看起来应该分两个阶段完成:1。获取产品的所有属性名称2.然后使用for循环中属性名称的服务器端代码编写代码”

答:不,看起来单个查询将返回产品的所有属性名称和值对。每个属性名称/值都在一个单独的行上。

无需使用“for循环”来针对数据库生成其他查询。是的,可以这样做,但完全没必要。

如果您有一些奇怪的要求,要构建另一个查询来针对数据库运行以返回您指定格式的结果集,那么无论您处理来自“更一般情况”语句的结果集的任何处理,它都可能是更有效地处理结果集,而不再对数据库运行任何查询。

如果需要返回如下所示的结果集:

 product_id height width
 ---------- ------ -----
          1 380    320

(如同组成另一个查询一样奇怪),完全有可能使用“更一般的查询”中的结果集来生成如下所示的查询:

SELECT 1 AS product_id, '380' AS height, '320' AS width

虽然这样的练习是毫无意义的,但鉴于你没有返回任何你以前没有返回的新信息,现在你有另外一个你需要处理的结果集,这在我看来只是一大堆不必要的开销。

答案 2 :(得分:1)

我首先要说这是一个非常糟糕的设计。根据您当前的方法,您需要运行多个子查询或与表别名连接以获得您想要的结果。

SELECT 
    product_id,
    (
        SELECT product_attribute.value 
        FROM product_attribute, attribute 
        WHERE product_attribute.product_id=product.product_id 
        AND product_attribute.attribute_id=attribute.attribute_id
        AND product_attribute.name = 'width'
    ) AS 'width',
    (
        SELECT product_attribute.value 
        FROM product_attribute, attribute 
        WHERE product_attribute.product_id=product.product_id 
        AND product_attribute.attribute_id=attribute.attribute_id
        AND product_attribute.name = 'height'
    ) AS 'height'
FROM
    product
ORDER BY 
    ...      

我建议:

attribute
   attribute_sid  (eg, string id)

product
   product_id
   name
   ...

product_attribute
   product_id   (foreign key to product table)
   attribute_sid  (foreign key to attribute table)
   value   

这样,您就拥有了一个明确的属性列表,以及每个产品的单个属性值。

SELECT attribute_sid, value FROM product_attribute WHERE product_id = 1

...将检索所有属性和值,这些属性和值可以方便地放在dictarraymap中。