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