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

时间:2016-06-01 12:08:05

标签: mysql sql database optimization filesort

在这个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;  

我已经在"工资"上创建了这个索引。表:

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  

3 个答案:

答案 0 :(得分:1)

我建议像这样编写查询:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr>
    <td>bla|1</td>
    <td>sdf</td>
    <td>hfgh</td>
  </tr>
  <tr>
    <td>bla|1</td>
    <td>3sf</td>
    <td>gdfg</td>
  </tr>
  <tr>
    <td>bla|1</td>
    <td>sdf</td>
    <td>jfhj</td>
  </tr>
  <tr>
    <td>bla|2</td>
    <td>sdf</td>
    <td>hfgh</td>
  </tr>
  <tr>
    <td>bla|2</td>
    <td>3sf</td>
    <td>gdfg</td>
  </tr>
  <tr>
    <td>bla|2</td>
    <td>sdf</td>
    <td>jfhj</td>
  </tr>
</table>

此查询可以利用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)上的索引。

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

这可能是10 - 15年前的一个很好的例子,但时代已经改变。

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

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

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