优化 MySQL 连接查询以删除 Using temporary 并使用索引?

Posted

技术标签:

【中文标题】优化 MySQL 连接查询以删除 Using temporary 并使用索引?【英文标题】:Optimize MySQL join query to remove Using temporary and use an index? 【发布时间】:2011-09-23 04:13:29 【问题描述】:

我有一个与ORDER BY name 的查询,name 上的索引被忽略。

如何优化查询以使用索引并从 EXPLAIN 中删除 Using temporary

我启用了log-queries-not-using-indexes,并且我看到这个查询数千次。

这是查询:

SELECT l.parent_id, j.id, j.location_id, j.currency, j.frequency, ROUND((j.salary_min + j.salary_max)/2) as salary 
FROM jobs AS j
JOIN location AS l
    ON j.location_id = l.id
WHERE j.salary_min !=0 
    AND j.status != 'Rejected'      
    AND l.published =1
    AND date_sub(now(), interval 1 month) <= j.effected_date
ORDER BY l.name

解释:

+----+-------------+-------+--------+----------------------------------+---------------+---------+----------------------------+------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                    | key           | key_len | ref                        | rows | Extra                                        |
+----+-------------+-------+--------+----------------------------------+---------------+---------+----------------------------+------+----------------------------------------------+
|  1 | SIMPLE      | j     | range  | effected_date,location_id,status | effected_date | 9       | NULL                       |  562 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | l     | eq_ref | PRIMARY                          | PRIMARY       | 4       | esljw_joomla.j.location_id |    1 | Using where                                  |
+----+-------------+-------+--------+----------------------------------+---------------+---------+----------------------------+------+----------------------------------------------+
2 rows in set (0.01 sec)

以及表结构:

CREATE TABLE IF NOT EXISTS `jobs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `location_id` varchar(255) NOT NULL,
  `status` varchar(255) DEFAULT NULL,
  `currency` varchar(255) DEFAULT NULL,
  `salary_min` int(11) DEFAULT NULL,
  `salary_max` int(11) DEFAULT NULL,
  `effected_date` datetime DEFAULT NULL,
  `frequency` varchar(255) NOT NULL DEFAULT '1',
  PRIMARY KEY (`id`),
  KEY `effected_date` (`effected_date`),
  KEY `location_id` (`location_id`),
  KEY `status` (`status`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=10130 ;

CREATE TABLE IF NOT EXISTS `location` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) DEFAULT NULL,
  `parent_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=304 ;

【问题讨论】:

【参考方案1】:
    published + id复合索引添加到location表中 将l.published =1 条件移至ON 子句

这是您可以针对您的情况执行的操作。但可能您永远不会摆脱 using temporary,因为您不是按主表排序,而是按联接表排序。

【讨论】:

你能解释一下把 l.published=1 移到 ON 子句是什么意思吗? @大卫罗杰斯:ON j.location_id = l.id AND l.published=1 嗯...我试过alter table location add index id_pub (id, published)alter table location add index pub_id (published, id)。 EXPLAIN 仍然选择主要的j.id 索引并且不使用新索引。【参考方案2】:

这是因为您首先列出了job。更改表格的顺序,如下所示:

SELECT l.parent_id, j.id, j.location_id, j.currency, j.frequency, ROUND((j.salary_min + j.salary_max)/2) as salary 
FROM location AS l
JOIN jobs AS j  ON j.location_id = l.id
WHERE j.salary_min !=0 
AND j.status != 'Rejected'      
AND l.published =1
AND date_sub(now(), interval 1 month) <= j.effected_date
ORDER BY l.name

试一试并发布结果。

【讨论】:

是的,我已经试过了。它仍然给出相同的解释输出。不过谢谢! =)【参考方案3】:

很多时候我已经完成了查询,其中您在查询中具有正确的主表,并且具有良好的索引,单独添加 STRAIGHT_JOIN 可以修复查询。因此,根据您现有的标准,您应该对日期索引感到满意,并将其用作主要标准......例如

SELECT STRAIGHT_JOIN
      L.Parent_ID, 
      J.id, 
      J.location_id, 
      J.currency, 
      J.frequency, 
      ROUND(( J.salary_min + J.salary_max) / 2 ) as Salary 
   FROM
      jobs J
         join Location L
            on J.Location_ID = L.ID
            AND L.Published = 1
   WHERE
          J.Effected_Date >= date_sub(now(), interval 1 month)
      AND J.salary_min != 0
      AND J.status != 'Rejected'
   ORDER BY 
      L.name

【讨论】:

哈,我什至从未听说过 STRAIGHT_JOIN,我得研究一下。 =) 查询效果很好,但 EXPLAIN 仍然显示 Using where; Using temporary; Using filesort

以上是关于优化 MySQL 连接查询以删除 Using temporary 并使用索引?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 索引优化与子查询与左连接

C#访问MySQL:连接查询删除(查删)

C#访问MySQL:连接查询删除(查删)

MySQL查询优化概念辨析---Using where 和 Using index

MySQL查询优化概念辨析---Using where 和 Using index

使用昂贵的 INNER JOIN 优化 MySQL 查询