优化 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查询优化概念辨析---Using where 和 Using index