Mysql慢查询:JOIN +多个WHERES + ORDER BY

chr*_*nch 7 mysql database indexing query-optimization

长时间潜伏,第一个问题!

我正在努力优化此查询,该查询选择与所选过滤器匹配的价格最低的商品:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all
WHERE (product_info.category = 2  
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13
Run Code Online (Sandbox Code Playgroud)

它解释说:

| id | select_type | table        | type   | possible_keys                                             | key     | key_len | ref                 | rows   | Extra                           |  
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | PRIMARY     | <derived2>   | ALL    | NULL                                                     | NULL    | NULL    | NULL                | 89801  | Using temporary; Using filesort | 
|  1 | PRIMARY     | product_info | eq_ref | PRIMARY,category_prod_id_retail_price,category_ret...     | PRIMARY | 4       | product_all.prod_id | 1      | Using where                     | 
|  2 | DERIVED     | product_all  | ref    | date_2                                                    | date_2  | 3       |                     | 144107 |                                 | 
Run Code Online (Sandbox Code Playgroud)

我已经尝试消除子查询,直觉看起来更好但实际上需要更长时间:

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link
FROM product_info
NATURAL JOIN product_all
WHERE (product_all.date = '2010-09-30'
AND product_info.category = 2 
AND product_info.gender = 'W' )
GROUP BY product_all.prod_id
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13
Run Code Online (Sandbox Code Playgroud)

它的解释是:

| id | select_type | table        | type | possible_keys                                             | key                      | key_len | ref                               | rows | Extra                                        |  
+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+  
|  1 | SIMPLE      | product_info | ref  | PRIMARY,category_prod_id_retail_price,category_ret...     | category_retail_price    | 5       | const                             | 269  | Using where; Using temporary; Using filesort | 
|  1 | SIMPLE      | product_all  | ref  | PRIMARY,prod_id,date_2                                    | prod_id                  | 4       | equipster_db.product_info.prod_id | 141  | Using where                                  | 
Run Code Online (Sandbox Code Playgroud)

以下是表格:

CREATE TABLE `product_all` (
`prod_id` INT( 10 ) NOT NULL PRIMARY KEY ,
`ref_id` INT( 10) NOT NULL PRIMARY KEY ,
`date` DATE NOT NULL ,
`buy_link` BLOB NOT NULL ,
`sale_price` FLOAT NOT NULL
) ENGINE = MYISAM ;


CREATE TABLE `product_info` (
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`prod_name` VARCHAR( 200 ) NOT NULL,
`brand` VARCHAR( 50 ) NOT NULL,
`retail_price` FLOAT NOT NULL
`category` INT( 3 ) NOT NULL,
`gender` VARCHAR( 1 ) NOT NULL,
`type` VARCHAR( 10 ) NOT NULL
) ENGINE = MYISAM ;
Run Code Online (Sandbox Code Playgroud)

我的问题:
- 哪种查询结构似乎是最优的?
什么索引会优化这个查询?
- 重要的是:在添加或删除WHERE子句或使用不同的ORDER BY时,索引方法如何更改,例如按%off排序:

ORDER BY (1-(MIN(product_all.sale_price)/product_info.retail_price)) DESC  
Run Code Online (Sandbox Code Playgroud)

编辑:两个查询的自然连接作用于prod_id(product_info中的一条记录可以在product_all中有多个实例,这就是为什么需要对它们进行分组)

Jon*_*nVD 4

索引在 mysql 中产生了巨大的差异,一个查询需要 15 分钟,如果使用一组错误的索引,则需要 0.2 秒,而使用正确的索引则需要 0.2 秒,但找到正确的平衡通常是问题所在。当然,如果没有一些示例数据,很难说下面的解决方案是否会节省您的时间,但理论上它应该可以。

为了回答您的问题,我将重新设计表格,如下所示:

CREATE TABLE `product_all` ( 
`prod_id` INT( 10 ) NOT NULL, 
`ref_id` INT( 10) NOT NULL, 
`date` DATE NOT NULL , 
`buy_link` BLOB NOT NULL , 
`sale_price` FLOAT NOT NULL,
PRIMARY KEY (prod_id, ref_id) ,
INDEX date_Index (`date` ASC),
UNIQUE INDEX prod_price_Index (prod_id ASC, sale_price ASC)
) ENGINE = MYISAM ; 


CREATE TABLE `product_info` ( 
`prod_id` INT( 10 ) NOT NULL AUTO_INCREMENT, 
`prod_name` VARCHAR( 200 ) NOT NULL, 
`brand` VARCHAR( 50 ) NOT NULL, 
`retail_price` FLOAT NOT NULL, 
`category` INT( 3 ) NOT NULL, 
`gender` VARCHAR( 1 ) NOT NULL, 
`type` VARCHAR( 10 ) NOT NULL,
PRIMARY KEY (prod_id) ,
UNIQUE INDEX prod_id_name_Index (prod_id ASC, prod_name ASC),
INDEX category_Index (category ASC),
INDEX gender_Index (gender ASC)
) ENGINE = MYISAM ;

SELECT product_info.*, MIN(product_all.sale_price) as sale_price, product_all.buy_link         
FROM product_info         
NATURAL JOIN (SELECT * FROM product_all WHERE product_all.date = '2010-09-30') as product_all         
WHERE (product_info.category = 2           
AND product_info.gender = 'W' )         
GROUP BY product_all.prod_id         
ORDER BY MIN(product_all.sale_price) ASC LIMIT 13        
Run Code Online (Sandbox Code Playgroud)

这里的性能提升是通过对正在连接的主要字段建立索引并在 where 子句中获得的。就我个人而言,我会选择您的第一个查询,因为当您认为它应该表现得更好时。

据我了解第一个和第二个查询中发生的情况:

  • 第一个查询在执行自然联接之前由子查询进行过滤,这意味着它仅联接结果数据而不是整个表。
  • 第二个查询是连接整个第二个表,然后将整个表的结果行过滤回您想要的内容。

根据经验,通常您希望在主要连接字段以及 where 子句中最常使用的字段上添加索引。我还在您想要定期查询的一些字段上放置了一些唯一索引,例如 prod_id_name_Index。

如果这不能提高您的性能,如果您可以发布一些虚拟数据来使用,我也许可以获得一个可以进行基准测试的更快的解决方案。

是一篇介绍 mysql 索引性能的文章,如果您想了解更多信息,值得一读。

祝你好运!

编辑:我第一次错过了你的最后一个问题,答案是,如果你对主要连接字段建立索引,然后更改为 where 只会稍微影响整体性能,但我放在表上的唯一索引应该考虑到您想要查询的大部分内容。要记住的主要事情是,如果您频繁查询或连接某个字段,那么它确实应该被索引,但是您不应该担心重新调整索引策略方面的小查询和顺序更改。