4

案子:

表:

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 设计。如果可能的话,请写下查询,以防我们不知道产品有哪些名称。

谢谢

4

3 回答 3

6

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

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'

这是小提琴

这是我个人更喜欢的使用 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

祝你好运。

于 2013-02-06T23:27:27.890 回答
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

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

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

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


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


跟进:

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

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

您(显然)尝试做的是在运行查询时返回可变数量的列。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

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

于 2013-02-06T23:29:51.977 回答
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.

于 2013-02-06T23:30:54.183 回答