如何使用索引优化我的 MySQL 查询

Posted

技术标签:

【中文标题】如何使用索引优化我的 MySQL 查询【英文标题】:How to optimize my MySQL query with an index 【发布时间】:2016-06-01 12:08:04 【问题描述】:

在这个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”表上创建了这个索引:

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 temporaryUsing 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  

【问题讨论】:

你无法避免这种排序。 ORDER BY 位于计算列上。 【参考方案1】:

我建议这样编写查询:

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) 上的索引。

这消除了聚合所需的排序。查询仍然需要对最终结果进行排序。

【讨论】:

@catmg97 。 . .您是否有理由不接受此答案?【参考方案2】:

您应该使用显式的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;

【讨论】:

【参考方案3】:

这在 10 到 15 年前可能是一个很好的例子,但时代变了。

使用 InnoDB 代替 MyISAM。考虑使用 utf8 而不是 latin1。在标题(to_date) 上添加索引。删除两个表中仅 (emp_no) 上的冗余索引。添加INDEX(title)

(我同意你应该使用较新的JOIN 语法。)

而且,如果不转换为 Gordon 的子查询版本,AVG 可能会出错。 (在开发样本时,该构造可能不存在。)

【讨论】:

以上是关于如何使用索引优化我的 MySQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何为MySQL查询优化选择最佳索引

mysql优化之索引优化

非常慢的 MySQL COUNT DISTINCT 查询,即使有索引——如何优化?

mysql--索引优化

MySQL索引原理及SQL优化

ElasticSearch中文索引优化问题