如何解决可以优化此MySQL查询吗?
| 我目前正在尝试优化一个MySQL查询,该查询在具有10,000+行的表上运行会有点慢。CREATE TABLE IF NOT EXISTS `person` (
`_id` int(11) unsigned NOT NULL AUTO_INCREMENT,`_oid` char(8) NOT NULL,`firstname` varchar(255) NOT NULL,`lastname` varchar(255) NOT NULL,PRIMARY KEY (`_id`),KEY `_oid` (`_oid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `person_cars` (
`_id` int(11) NOT NULL AUTO_INCREMENT,`idx` varchar(255) NOT NULL,`val` blob NOT NULL,KEY `_oid` (`_oid`),KEY `idx` (`idx`),KEY `val` (`val`(64))
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
# Insert some 10000+ rows…
INSERT INTO `person` (`_oid`,`firstname`,`lastname`)
VALUES
(\'1\',\'John\',\'Doe\'),(\'2\',\'Jack\',\'Black\'),(\'3\',\'Jim\',\'Kirk\'),(\'4\',\'Forrest\',\'Gump\');
INSERT INTO `person_cars` (`_oid`,`idx`,`val`)
VALUES
(\'1\',\'0\',\'BMW\'),(\'1\',\'1\',\'PORSCHE\'),\'MERCEDES\'),\'TOYOTA\'),\'NISSAN\'),\'OLDMOBILE\');
SELECT `_person`.`_oid`,`_person`.`firstname`,`_person`.`lastname`,`_person_cars`.`cars[0]`,`_person_cars`.`cars[1]`
FROM `person` `_person`
LEFT JOIN (
SELECT `_person`.`_oid`,IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=0,`_person_cars`.`val`,NULL)),NULL) AS `cars[0]`,IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=1,NULL) AS `cars[1]`
FROM `person` `_person`
JOIN `person_cars` `_person_cars` ON `_person`.`_oid` = `_person_cars`.`_oid`
GROUP BY `_person`.`_oid`
) `_person_cars` ON `_person_cars`.`_oid` = `_person`.`_oid`
WHERE `cars[0]` = \'BMW\' OR `cars[1]` = \'BMW\';
在运行MySQL 5.1.53的虚拟机上,上述SELECT查询需要约170ms的时间。与约。两个表中的每个表有10,000行。
当我解释以上查询时,结果会有所不同,具体取决于每个表中有多少行:
+----+-------------+--------------+-------+---------------+------+---------+------+------+---------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+-------+---------------+------+---------+------+------+---------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 4 | Using where |
| 1 | PRIMARY | _person | ALL | _oid | NULL | NULL | NULL | 4 | Using where; Using join buffer |
| 2 | DERIVED | _person_cars | ALL | _oid | NULL | NULL | NULL | 7 | Using temporary; Using filesort |
| 2 | DERIVED | _person | index | _oid | _oid | 24 | NULL | 4 | Using where; Using index; Using join buffer |
+----+-------------+--------------+-------+---------------+------+---------+------+------+---------------------------------------------+
大约10,000行给出以下结果:
+----+-------------+--------------+------+---------------+------+---------+------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------+------+---------------+------+---------+------------------------+------+---------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 6613 | Using where |
| 1 | PRIMARY | _person | ref | _oid | _oid | 24 | _person_cars._oid | 10 | |
| 2 | DERIVED | _person_cars | ALL | _oid | NULL | NULL | NULL | 9913 | Using temporary; Using filesort |
| 2 | DERIVED | _person | ref | _oid | _oid | 24 | test._person_cars._oid | 10 | Using index |
+----+-------------+--------------+------+---------------+------+---------+------------------------+------+---------------------------------+
当我省略WHERE子句或当我左联接另一个类似于person_cars
的表时,情况会变得更糟。
有谁知道如何优化SELECT查询以使事情更快一些?
解决方法
这很慢,因为这将对随后合并在一起的人员进行三次全表扫描:
LEFT JOIN (
...
GROUP BY `_person`.`_oid` -- the group by here
) `_person_cars` ...
WHERE ... -- and the where clauses on _person_cars.
考虑到where子句,左连接实际上是一个内部连接。您可以在与人实际交往之前就先推条件。该连接也被不必要地应用了两次。
这将使其速度更快,但是由于子查询中的group by,如果您具有order by / limit子句,它仍将导致对人员进行全表扫描(即仍然不佳):
JOIN (
SELECT `_person_cars`.`_oid`,IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=0,`_person_cars`.`val`,NULL)),NULL) AS `cars[0]`,IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=1,NULL) AS `cars[1]`
FROM `person_cars`
GROUP BY `_person_cars`.`_oid`
HAVING IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=0,NULL) = \'BMW\' OR
IFNULL(GROUP_CONCAT(IF(`_person_cars`.`idx`=1,NULL) = \'BMW\'
) `_person_cars` ... -- smaller number of rows
如果您按/限制应用订单,则通过两次查询,您将获得更好的结果,即:
SELECT `_person`.`_oid`,`_person`.`firstname`,`_person`.`lastname`
FROM `_person`
JOIN `_person_cars`
ON `_person_cars`.`_oid` = `_person`.`_oid`
AND `_person_cars`.`val` = \'BMW\'
GROUP BY -- pre-sort the result before grouping,so as to not do the work twice
`_person`.`lastname`,-- eliminate users with multiple BMWs
`_person`.`_oid`
ORDER BY `_person`.`lastname`,`_person`.`_oid`
LIMIT 10
然后使用结果ID选择带有IN()子句的汽车。
哦,您的vals
列可能应该是varchar。
,检查这个
SELECT
p._oid AS oid,p.firstname AS firstname,p.lastname AS lastname,pc.val AS car1,pc2.val AS car2
FROM person AS p
LEFT JOIN person_cars AS pc
ON pc._oid = p._oid
AND pc.idx = 0
LEFT JOIN person_cars AS pc2
ON pc2._oid = p._oid
AND pc2.idx = 1
WHERE pc.val = \'BMW\'
OR pc2.val = \'BWM\'
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。