[英]Optimize SQL query in MySQL 5.6
系统是Amazon RDS(MySql 5.6.x),基于Moodle 2.8的软件,这是我目前正在处理的表:
CREATE TABLE `mdl_course_categories` (
`id` bigint(10) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL DEFAULT '',
`idnumber` varchar(100) DEFAULT NULL,
`description` longtext,
`descriptionformat` tinyint(2) NOT NULL DEFAULT '0',
`parent` bigint(10) NOT NULL DEFAULT '0',
`sortorder` bigint(10) NOT NULL DEFAULT '0',
`coursecount` bigint(10) NOT NULL DEFAULT '0',
`visible` tinyint(1) NOT NULL DEFAULT '1',
`visibleold` tinyint(1) NOT NULL DEFAULT '1',
`timemodified` bigint(10) NOT NULL DEFAULT '0',
`depth` bigint(10) NOT NULL DEFAULT '0',
`path` varchar(255) NOT NULL DEFAULT '',
`theme` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `mdl_courcate_par_ix` (`parent`),
KEY `mdl_carcoute_tmid` (`timemodified`,`id`),
KEY `mdl_tm_field` (`timemodified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
我原来的查询是这样的:
SELECT
id,
name,
description,
FROM_UNIXTIME(timemodified) AS timemodified,
timemodified AS traw
FROM
mdl_course_categories
WHERE
timemodified BETWEEN 1360602072 AND 1446736233
OR
id > 0
ORDER BY id ASC
LIMIT 0 , 50000
该查询的解释是:
+----+-------------+-----------------------+-------+----------------------------------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-----------------------+-------+----------------------------------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | mdl_course_categories | index | PRIMARY,mdl_carcoute_tmid,mdl_tm_field | PRIMARY | 8 | NULL | 68 | 100.00 | Using where |
+----+-------------+-----------------------+-------+----------------------------------------+---------+---------+------+------+----------+-------------+
数据库专家建议我避免OR并将查询转换为:
SELECT * FROM
(
(
SELECT
id,
name,
description,
FROM_UNIXTIME(timemodified) AS timemodified,
timemodified AS traw
FROM
mdl_course_categories
WHERE
timemodified BETWEEN 1360602072 AND 1446736233
)
UNION ALL
(
SELECT
id,
name,
description,
FROM_UNIXTIME(timemodified) AS timemodified,
timemodified AS traw
FROM
mdl_course_categories
WHERE
id > 0
)
) t
ORDER BY id ASC
LIMIT 0 , 50000
解释如下:
+----+--------------+-----------------------+-------+--------------------------------+-------------------+---------+------+------+----------+-----------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+-----------------------+-------+--------------------------------+-------------------+---------+------+------+----------+-----------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 80 | 100.00 | Using filesort |
| 2 | DERIVED | mdl_course_categories | range | mdl_carcoute_tmid,mdl_tm_field | mdl_carcoute_tmid | 8 | NULL | 12 | 100.00 | Using index condition |
| 3 | UNION | mdl_course_categories | range | PRIMARY | PRIMARY | 8 | NULL | 68 | 100.00 | Using where |
| NULL | UNION RESULT | <union2,3> | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+-----------------------+-------+--------------------------------+-------------------+---------+------+------+----------+-----------------------+
你怎么看? 这可以做得更好吗?
最佳查询将在子查询中包括limit
和order by
,并使用正确的索引:
SELECT *
FROM ((SELECT id, name, description,
FROM_UNIXTIME(timemodified) AS timemodified, timemodified AS traw
FROM mdl_course_categories
WHERE timemodified BETWEEN 1360602072 AND 1446736233
ORDER BY id
LIMIT 50000
) UNION ALL
(SELECT id, name, description,
FROM_UNIXTIME(timemodified) AS timemodified, timemodified AS traw
FROM mdl_course_categories
WHERE id > 0 AND
NOT timemodified BETWEEN 1360602072 AND 1446736233
ORDER BY id
LIMIT 50000
)
) t
ORDER BY id ASC
LIMIT 0 , 50000;
您想要的索引是mdl_course_categories(id, timemodified)
和mdl_course_categories(timemodified)
。 不幸的是,由于where
子句具有不等式,因此您不一定必须退出排序。 但是,排序100,000条记录应该比所有记录都要好。
请注意,第二个WHERE
子句已更改为从第一WHERE
句中排除记录。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.