通过将子查询嵌套在一起来解决MySQL中的61个表JOIN限制

时间:2013-11-22 17:54:39

标签: mysql magento join left-join entity-attribute-value

我发现你可以使用子查询来解决MySQL中的61个表连接限制。 https://stackoverflow.com/a/20134402/2843690

我正在试图找出如何轻松在我正在努力从Magento获取详细产品清单的程序中使用它(但我认为这个问题的答案可能适用于涉及eav的很多情况)。需要连接的表看起来像这样:

catalog_product_entity
+-----------+----------------+
| entity_id | entity_type_id |
+-----------+----------------+
|         1 |              4 |
|         2 |              4 |
|         3 |              4 |
|         4 |              4 |
|         5 |              4 |
|         6 |              4 |
|         7 |              4 |
|         8 |              4 |
|         9 |              4 |
+-----------+----------------+

catalog_product_entity_int
+----------+----------------+--------------+-----------+-------+
| value_id | entity_type_id | attribute_id | entity_id | value |
+----------+----------------+--------------+-----------+-------+
|        1 |              4 |            2 |         1 |   245 |
|        2 |              4 |            3 |         1 |   250 |
|        3 |              4 |            4 |         1 |   254 |
|        4 |              4 |            2 |         2 |   245 |
|        5 |              4 |            3 |         2 |   249 |
|        6 |              4 |            4 |         2 |   253 |
|        7 |              4 |            2 |         3 |   247 |
|        8 |              4 |            3 |         3 |   250 |
|        9 |              4 |            4 |         3 |   254 |
+----------+----------------+--------------+-----------+-------+

eav_attribute
+--------------+----------------+----------------+--------------+
| attribute_id | entity_type_id | attribute_code | backend_type |
+--------------+----------------+----------------+--------------+
|            1 |              4 | name           | varchar      |
|            2 |              4 | brand          | int          |
|            3 |              4 | color          | int          |
|            4 |              4 | size           | int          |
|            5 |              4 | price          | decimal      |
|            6 |              4 | cost           | decimal      |
|            7 |              4 | created_at     | datetime     |
|            8 |              3 | name           | varchar      |
|            9 |              3 | description    | text         |
+--------------+----------------+----------------+--------------+

eav_attribute_option
+-----------+--------------+
| option_id | attribute_id |
+-----------+--------------+
|       245 |            2 |
|       246 |            2 |
|       247 |            2 |
|       248 |            3 |
|       249 |            3 |
|       250 |            3 |
|       251 |            4 |
|       252 |            4 |
|       253 |            4 |
|       254 |            4 |
+-----------+--------------+

eav_attribute_option_value
+----------+-----------+-------------------+
| value_id | option_id |       value       |
+----------+-----------+-------------------+
|       15 |       245 | Fruit of the Loom |
|       16 |       246 | Hanes             |
|       17 |       247 | Jockey            |
|       18 |       248 | White             |
|       19 |       249 | Black             |
|       20 |       250 | Gray              |
|       21 |       251 | Small             |
|       22 |       252 | Medium            |
|       23 |       253 | Large             |
|       24 |       254 | Extra Large       |
+----------+-----------+-------------------+

我正在编写的程序生成的SQL查询看起来像这样:

SELECT cpe.entity_id
, brand_int.value as brand_int, brand.value as brand
, color_int.value as color_int, color.value as color
, size_int.value as size_int, size.value as size

FROM catalog_product_entity as cpe

LEFT JOIN catalog_product_entity_int as brand_int
ON (cpe.entity_id = brand_int.entity_id
AND brand_int.attribute_id = 2)
LEFT JOIN eav_attribute_option as brand_option
ON (brand_option.attribute_id = 2
AND brand_int.value = brand_option.option_id)
LEFT JOIN eav_attribute_option_value as brand
ON (brand_option.option_id = brand.option_id)

LEFT JOIN catalog_product_entity_int as color_int
ON (cpe.entity_id = color_int.entity_id
AND color_int.attribute_id = 3)
LEFT JOIN eav_attribute_option as color_option
ON (color_option.attribute_id = 3
AND color_int.value = color_option.option_id)
LEFT JOIN eav_attribute_option_value as color
ON (color_option.option_id = color.option_id)

LEFT JOIN catalog_product_entity_int as size_int
ON (cpe.entity_id = size_int.entity_id
AND size_int.attribute_id = 4)
LEFT JOIN eav_attribute_option as size_option
ON (size_option.attribute_id = 4
AND size_int.value = size_option.option_id)
LEFT JOIN eav_attribute_option_value as size
ON (size_option.option_id = size.option_id)
;

编写代码以生成查询相对容易,查询相当容易理解;但是,我很容易达到61表加入限制,这是我用现实数据做的。我相信数学说21个整数类型属性将超过限制,这是在我开始添加varchar,text和decimal属性之前。

因此,我提出的解决方案是使用子查询来克服61表限制。

实现此目的的一种方法是将连接分组为61个连接的子查询。然后所有的小组都会加入。我想我可以弄清楚sql查询应该是什么样子,但是编写代码来生成查询似乎很困难。还有一个(虽然是理论上的)问题,如果有足够的属性,可能会再次违反61表限制。换句话说,如果我有62组61个表,则会出现MySQL错误。显然,人们可以通过将组的组分组到61来解决这个问题。但这只会使代码更难以编写和理解。

我认为我想要的解决方案是在子查询中嵌套子查询,以便每个子查询使用2个表(或一个表和一个子查询)的单个连接。直觉上,似乎代码对于这种查询更容易编写。不幸的是,考虑这些查询应该是什么样子会让我的大脑受到伤害。这就是我需要帮助的原因。

这样的MySQL查询会是什么样的?

1 个答案:

答案 0 :(得分:2)

通过EAV设计加入太多属性是正确的,可能会超过联接的限制。甚至在此之前,可能存在实际的连接限制,因为这么多连接的成本在几何上变得越来越高。这有多糟糕取决于你的服务器的容量,但它可能比61低很多。

因此,查询EAV数据模型以产生结果,好像它存储在传统的关系模型中(每个属性一列)是有问题的。

解决方案:不要使用每个属性的连接,这意味着您不能指望以纯粹使用SQL的传统的每实体行格式生成结果。

我对Magento架构并不熟悉,但我可以从您的查询中推断出这样的东西可能有用:

SELECT cpe.entity_id
, o.value AS option
, v.value AS option_value
FROM catalog_product_entity AS cpe
INNER JOIN catalog_product_entity_int AS i 
  ON cpe.entity_id = i.entity_id AND i.attribute_id IN (2,3,4)
INNER JOIN eav_attribute_option AS o 
  ON i.value = o.option_id AND i.attribute_id = o.attribute_id
INNER JOIN eav_attribute_option_value AS v
  ON v.option_id = o.option_id;

IN(2,3,4,...)谓词是指定多个属性的位置。无需添加更多连接即可获得更多属性。它们只是作为行而不是列返回。

这意味着您必须编写应用程序代码来获取此结果集的所有行,并将它们映射到单个对象的字段中。

来自@Axel的评论,听起来像Magento提供辅助函数来执行此操作以消耗结果集并将其映射到对象中。