如何使用索引优化我的 MySQL 查询
How to optimize my MySQL query with an index
关于这个 database(来自 MySQL 个样本的员工)我必须使用索引优化此查询:
SELECT t.title,
Avg(s.salary) salario_medio
FROM titles t,
salaries s
WHERE t.emp_no = s.emp_no
AND t.to_date > Now()
AND s.to_date > Now()
GROUP BY t.title
ORDER BY salario_medio DESC;
我已经在 "salaries" table:
上创建了这个索引
CREATE INDEX to_date_idx ON salaries(to_date);
但是 EXPLAIN
给了我那些行:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: s
type: range
possible_keys:PRIMARY,emp_no,to_date_idx
key: to_date_idx
key_len: 3
ref: NULL
rows: 370722
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t
type: ref
possible_keys: PRIMARY,emp_no
key: emp_no
key_len: 4
ref: employees.s.emp_no
rows: 1
Extra: Using where
我不想使用 Using temporary
和 Using filesort
。
信息:
SHOW CREATE TABLE salaries;
CREATE TABLE `salaries`
(
`emp_no` INT(11) NOT NULL,
`salary` INT(11) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE NOT NULL,
PRIMARY KEY (`emp_no`, `from_date`),
KEY `emp_no` (`emp_no`),
KEY `to_date_idx` (`to_date`)
) engine=myisam DEFAULT charset=latin1 show CREATE TABLE titles;
CREATE TABLE `titles`
(
`emp_no` INT(11) NOT NULL,
`title` VARCHAR(50) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE DEFAULT NULL,
PRIMARY KEY (`emp_no`, `title`, `from_date`),
KEY `emp_no` (`emp_no`)
)
engine=myisam
DEFAULT charset=latin1
我建议这样写查询:
SELECT t.title,
(SELECT AVG(s.salary)
FROM salaries s
WHERE t.emp_no = s.emp_no AND
s.to_date > NOW()
) as salario_medio
FROM titles t
WHERE t.to_date > NOW()
ORDER BY salario_medio DESC;
此查询可以利用 titles(to_date, title, emp_no)
和 salaries(emp_no, to_date)
上的索引。
这消除了聚合所需的排序。查询仍然需要对最终结果进行排序。
您应该使用明确的 join
语法,而不是隐式的。但这对性能没有帮助。将函数 NOW()
的结果捕获到一个变量中会有所帮助,这样它只需计算一次,而不是每行计算两次:
DECLARE @dtNOW DATETIME = NOW()
SELECT t.title
, AVG(s.salary) salario_medio
FROM titles t
INNER JOIN salaries s
ON t.emp_no = s.emp_no
WHERE t.to_date > dtNOW
AND s.to_date > dtNOW
GROUP BY t.title
ORDER BY salario_medio DESC;
这在 10-15 年前可能是一个很好的例子,但时代变了。
使用 InnoDB 而不是 MyISAM。考虑使用 utf8 而不是 latin1。在标题 (to_date)
上添加索引。删除两个表中仅 (emp_no)
上的冗余索引。添加 INDEX(title)
.
(我同意您应该使用更新的 JOIN
语法。)
而且,如果不转换为 Gordon 的子查询版本,AVG
很可能是错误的。 (开发示例时可能不存在该构造。)
关于这个 database(来自 MySQL 个样本的员工)我必须使用索引优化此查询:
SELECT t.title,
Avg(s.salary) salario_medio
FROM titles t,
salaries s
WHERE t.emp_no = s.emp_no
AND t.to_date > Now()
AND s.to_date > Now()
GROUP BY t.title
ORDER BY salario_medio DESC;
我已经在 "salaries" table:
上创建了这个索引CREATE INDEX to_date_idx ON salaries(to_date);
但是 EXPLAIN
给了我那些行:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: s
type: range
possible_keys:PRIMARY,emp_no,to_date_idx
key: to_date_idx
key_len: 3
ref: NULL
rows: 370722
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t
type: ref
possible_keys: PRIMARY,emp_no
key: emp_no
key_len: 4
ref: employees.s.emp_no
rows: 1
Extra: Using where
我不想使用 Using temporary
和 Using filesort
。
信息:
SHOW CREATE TABLE salaries;
CREATE TABLE `salaries`
(
`emp_no` INT(11) NOT NULL,
`salary` INT(11) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE NOT NULL,
PRIMARY KEY (`emp_no`, `from_date`),
KEY `emp_no` (`emp_no`),
KEY `to_date_idx` (`to_date`)
) engine=myisam DEFAULT charset=latin1 show CREATE TABLE titles;
CREATE TABLE `titles`
(
`emp_no` INT(11) NOT NULL,
`title` VARCHAR(50) NOT NULL,
`from_date` DATE NOT NULL,
`to_date` DATE DEFAULT NULL,
PRIMARY KEY (`emp_no`, `title`, `from_date`),
KEY `emp_no` (`emp_no`)
)
engine=myisam
DEFAULT charset=latin1
我建议这样写查询:
SELECT t.title,
(SELECT AVG(s.salary)
FROM salaries s
WHERE t.emp_no = s.emp_no AND
s.to_date > NOW()
) as salario_medio
FROM titles t
WHERE t.to_date > NOW()
ORDER BY salario_medio DESC;
此查询可以利用 titles(to_date, title, emp_no)
和 salaries(emp_no, to_date)
上的索引。
这消除了聚合所需的排序。查询仍然需要对最终结果进行排序。
您应该使用明确的 join
语法,而不是隐式的。但这对性能没有帮助。将函数 NOW()
的结果捕获到一个变量中会有所帮助,这样它只需计算一次,而不是每行计算两次:
DECLARE @dtNOW DATETIME = NOW()
SELECT t.title
, AVG(s.salary) salario_medio
FROM titles t
INNER JOIN salaries s
ON t.emp_no = s.emp_no
WHERE t.to_date > dtNOW
AND s.to_date > dtNOW
GROUP BY t.title
ORDER BY salario_medio DESC;
这在 10-15 年前可能是一个很好的例子,但时代变了。
使用 InnoDB 而不是 MyISAM。考虑使用 utf8 而不是 latin1。在标题 (to_date)
上添加索引。删除两个表中仅 (emp_no)
上的冗余索引。添加 INDEX(title)
.
(我同意您应该使用更新的 JOIN
语法。)
而且,如果不转换为 Gordon 的子查询版本,AVG
很可能是错误的。 (开发示例时可能不存在该构造。)