请教我如何优化此 MySQL 查询

Please teach me how to optimize this MySQL query

我有两个 table,main_part(3k 条记录)和 part_details(25k 条记录)

我尝试了以下索引,但 explain 总是 returns 完整 table 扫描 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 table 中的主键。这会是罪魁祸首吗?

创建 tables 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;

对于您的查询,更重要的索引是 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 添加索引检查解释计划,然后开始添加条件并在需要时更新索引。

对于此查询:

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.

的排序并不容易

似乎 part_details 中用于筛选的大多数列都不是很有选择性(显示可能是 On/off 开关,国家/地区在许多产品中可能非常相似,等等)。

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

我也会尝试创建这个索引,如果有任何变化,请检查解释计划:

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