Mysql - 优化 - 使用 having 的多个 group_concat 和连接

标签 mysql optimization group-concat having

我查看了类似的 group_concat mysql 优化线程,但似乎没有一个与我的问题相关,我的 mysql 知识被这个线程扩展了。

我的任务是提高脚本的速度,其中包含一个非常繁重的 Mysql 查询。

所讨论的查询使用 GROUP_CONCAT 创建与特定产品相关的颜色、标签和尺寸列表。然后它使用 HAVING/FIND_IN_SET 来过滤这些串联列表以查找属性,由用户控件设置并显示结果。

在下面的示例中,它正在查找 product_tag=1、product_colour=18 和 product_size=17 的所有产品。所以这可能是适合男性(标签)的中号(尺寸)蓝色产品(颜色)。

shop_products 表包含大约 3500 行,所以不是特别大,但下面的执行大约需要 30 秒。它适用于 1 或 2 个连接,但添加第三个连接只会杀死它。

SELECT shop_products.id, shop_products.name, shop_products.default_image_id, 
GROUP_CONCAT( DISTINCT shop_product_to_colours.colour_id ) AS product_colours, 
GROUP_CONCAT( DISTINCT shop_products_to_tag.tag_id ) AS product_tags, 
GROUP_CONCAT( DISTINCT shop_product_colour_to_sizes.tag_id ) AS product_sizes
FROM shop_products
LEFT JOIN shop_product_to_colours ON shop_products.id = shop_product_to_colours.product_id
LEFT JOIN shop_products_to_tag ON shop_products.id = shop_products_to_tag.product_id
LEFT JOIN shop_product_colour_to_sizes ON shop_products.id = shop_product_colour_to_sizes.product_id
WHERE shop_products.category_id =  '50'
GROUP BY shop_products.id
HAVING((FIND_IN_SET( 1, product_tags ) >0) 
AND(FIND_IN_SET( 18, product_colours ) >0)
AND(FIND_IN_SET( 17, product_sizes ) >0))
ORDER BY shop_products.name ASC 
LIMIT 0 , 30

我希望有人通常可以建议一种更好的方法来构建此查询,而无需重新构建数据库(如果没有数周的数据迁移和脚本更改,目前这不是一个真正的选择)?或任何关于优化的一般建议。当前使用 explain 返回以下内容(如您所见,索引无处不在!)。

id  select_type table                          type possible_keys                         key           key_len ref rows            Extra   
1   SIMPLE      shop_products                  ref  category_id,category_id_2             category_id   2   const   3225    Using where; Using temporary; Using filesort
1   SIMPLE      shop_product_to_colours        ref  product_id,product_id_2,product_id_3  product_id    4   candymix_db.shop_products.id    13  
1   SIMPLE      shop_products_to_tag           ref  product_id,product_id_2               product_id    4   candymix_db.shop_products.id    4   
1   SIMPLE      shop_product_colour_to_sizes   ref  product_id                            product_id    4   candymix_db.shop_products.id    133 

最佳答案

重写查询以使用 WHERE 而不是 HAVING。因为WHERE是在MySQL对行进行查找时应用的,它可以使用索引。 HAVING 在选择行后应用以过滤已选择的结果。 HAVING 按设计不能使用索引。
例如,您可以这样做:

SELECT p.id, p.name, p.default_image_id, 
    GROUP_CONCAT( DISTINCT pc.colour_id ) AS product_colours, 
    GROUP_CONCAT( DISTINCT pt.tag_id ) AS product_tags, 
    GROUP_CONCAT( DISTINCT ps.tag_id ) AS product_sizes
FROM shop_products p
    JOIN shop_product_to_colours pc_test ON p.id = pc_test.product_id AND pc_test.colour_id = 18
    JOIN shop_products_to_tag pt_test ON p.id = pt_test.product_id AND pt_test.tag_id = 1
    JOIN shop_product_colour_to_sizes ps_test ON p.id = ps_test.product_id AND ps_test.tag_id = 17
    JOIN shop_product_to_colours pc ON p.id = pc.product_id
    JOIN shop_products_to_tag pt ON p.id = pt.product_id
    JOIN shop_product_colour_to_sizes ps ON p.id = ps.product_id
WHERE p.category_id =  '50'
GROUP BY p.id
ORDER BY p.name ASC

更新

我们将加入每张 table 两次。
首先检查它是否包含一些值(来自 FIND_IN_SET 的条件)。
第二次联接将为 GROUP_CONCAT 生成数据,以从表中选择所有产品值。

更新2

正如@Matt Raines 评论的那样,如果我们不需要使用 GROUP_CONCAT 列出产品值,查询会变得更加简单:

SELECT p.id, p.name, p.default_image_id
FROM shop_products p
    JOIN shop_product_to_colours pc ON p.id = pc.product_id
    JOIN shop_products_to_tag pt ON p.id = pt.product_id
    JOIN shop_product_colour_to_sizes ps ON p.id = ps.product_id
WHERE p.category_id =  '50'
    AND (pc.colour_id = 18 AND pt.tag_id = 1 AND ps.tag_id = 17)
GROUP BY p.id
ORDER BY p.name ASC

这将选择具有三个过滤属性的所有产品。

关于Mysql - 优化 - 使用 having 的多个 group_concat 和连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37413701/

相关文章:

mysql - GROUP CONCAT 由于某种原因不工作

mysql - 具有相同值但处于不同状态/问题的单个或多个 ID

MYSQL 从具有 100 万行的表中选择 distinct(indexed_column)

php - 从按两列分组的表中获取所有数据 - mysql、php

c++ - 高效的目标矩形源矩形裁剪

java - 将一个 int 乘以 30、31、32 - 这些真的被编译器优化了吗? (有效的java这么说)

mysql - 时间和日期字段与 ID 字段不同步

Mysql select If else if 查询三个值

php - 如何从数据库中选择具有相同查询但表中不同条件的数据?

python - 从 MySQL 数据库中选择结果并将其分配给变量