请教我如何优化这个MySQL查询

时间:2017-11-22 13:43:58

标签: mysql sql query-optimization primary-key

我有两张表,main_part(3k记录)和part_details(25k记录)

我尝试了以下索引,但explain始终返回25k记录的全表扫描,而不是大约2k匹配记录和Using where; Using temporary; Using filesort

ALTER TABLE `main_part` ADD INDEX `main_part_index_1` (`unit`);
ALTER TABLE `part_details` ADD INDEX `part_details_index_1` (`approved`, `display`, `country`, `id`, `price`);

这是我的问题:

SELECT a.part_id, b.my_title, 
       b.price, a.type, 
       a.unit, a.certification, 
       b.my_image, 
       b.price/a.unit AS priceW 
FROM main_part AS a
INNER JOIN part_details AS b ON a.part_id=b.id
WHERE b.approved = 'Yes' 
AND b.display = 'On' 
AND b.country = 'US' 
AND a.unit >= 300 
ORDER BY b.price ASC LIMIT 50

我知道的一件事是a.part_id不是main_part表中的主键。这可能是罪魁祸首吗?

创建表SQL:

CREATE TABLE `main_part` (
  `id` smallint(6) NOT NULL AUTO_INCREMENT,
  `part_id` mediumint(9) NOT NULL DEFAULT '0',
  `type` varchar(50) NOT NULL DEFAULT '',
  `unit` varchar(50) NOT NULL DEFAULT '',
  `certification` varchar(50) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `part_details` (
  `id` mediumint(9) NOT NULL AUTO_INCREMENT,
  `asn` varchar(50) NOT NULL DEFAULT '',
  `country` varchar(5) NOT NULL DEFAULT '',
  `my_title` varchar(200) NOT NULL DEFAULT '',
  `display` varchar(5) NOT NULL DEFAULT 'On',
  `approved` varchar(5) NOT NULL DEFAULT 'No',
  `price` decimal(7,3) NOT NULL DEFAULT '0.000',
  `my_image` varchar(250) NOT NULL DEFAULT '',
  `update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `countryasn` (`country`,`asn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

3 个答案:

答案 0 :(得分:3)

对于您的查询,更重要的索引是JOIN条件,因为您已经知道a.part_id不是主键,所以没有默认索引和您的第一个尝试应该是:

ALTER TABLE `main_part` ADD INDEX `main_part_index_1` (`part_id`,`unit`);

因为我们首先对JOIN条件感兴趣,所以您还应该将第二个索引更改为

ALTER TABLE `part_details` ADD INDEX `part_details_index_1` 
            (`id`, `approved`, `display`, `country`, `price`);

订单在索引

中很重要

另一个提示是从基本查询开始:

SELECT *
FROM main_part AS a
INNER JOIN part_details AS b ON a.part_id=b.id

part_idid添加索引,检查解释计划,然后开始添加条件并在需要时更新索引。

答案 1 :(得分:1)

似乎用于过滤part_details的大多数列都没有选择性(显示可能是一个开/关开关,国家在许多产品中可能非常相似,等等。)

在某些情况下,当WHERE子句选择性不高时,MySQL可能会选择使用更适合ORDER BY子句的索引。

我也会尝试创建此索引并检查解释计划是否有任何更改:

ALTER TABLE `part_details` ADD INDEX `part_details_price_index` (`price`);

答案 2 :(得分:0)

对于此查询:

SELECT mp.part_id, pd.my_title, pd.price, mp.type, 
       mp.unit, mp.certification, pd.my_image, 
       pd.price/mp.unit AS priceW 
FROM main_part mp INNER JOIN
     part_details pd
     ON mp.part_id = pd.id
WHERE pd.approved = 'Yes' AND
      pd.display = 'On' AND
      pd.country = 'US' AND
      mp.unit >= 300 
ORDER BY pd.price ASC
LIMIT 50;

对于此查询,我将从part_details(country, display, approved, id, price)main_part(part_id, unit)上的索引开始。

part_details上的索引可用于join之前的过滤。要摆脱order by的排序并不容易。