我无法优化涉及GROUP BY,ORDER BY和LIMIT的相对简单的查询。该表有超过300,000条记录。这是架构(我添加了一些额外的索引来试验):
CREATE TABLE `scrape_search_results` (
`id` int(11) NOT NULL auto_increment,
`creative_id` int(11) NOT NULL,
`url_id` int(11) NOT NULL,
`access_date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `creative_url_index` (`creative_id`,`url_id`),
KEY `access_date_index` (`access_date`),
KEY `access_date_creative_id_index` (`access_date`,`creative_id`),
KEY `creative_id_access_date_index` (`creative_id`,`access_date`),
KEY `test_index` USING HASH (`creative_id`)
) ENGINE=MyISAM AUTO_INCREMENT=4252725 DEFAULT CHARSET=latin1
在表格中,单个creative_id
可能会出现多次(数百次)。我想回答的问题是一个相对简单的问题;给我creative_ids
订购的前20 access_date
。这是我的SQL:
SELECT `ScrapeSearchResult`.`creative_id`,
MAX(`ScrapeSearchResult`.`access_date`) AS `latest_access_date`
FROM `scrape_search_results` AS `ScrapeSearchResult`
WHERE 1 = 1
GROUP BY `ScrapeSearchResult`.`creative_id`
ORDER BY `latest_access_date` DESC
LIMIT 20;
以下是执行此查询的结果,我们看到第20个最大的access_date
是2010-08-23 11:03:25:
+-------------+---------------------+
| creative_id | latest_access_date |
+-------------+---------------------+
| 550 | 2010-08-23 11:07:49 |
| 4568 | 2010-08-23 11:07:49 |
| 552 | 2010-08-23 11:07:49 |
| 2109 | 2010-08-23 11:07:49 |
| 5221 | 2010-08-23 11:07:49 |
| 1544 | 2010-08-23 11:07:49 |
| 1697 | 2010-08-23 11:07:49 |
| 554 | 2010-08-23 11:07:12 |
| 932 | 2010-08-23 11:05:48 |
| 11029 | 2010-08-23 11:05:37 |
| 11854 | 2010-08-23 11:05:27 |
| 11856 | 2010-08-23 11:05:05 |
| 702 | 2010-08-23 11:03:56 |
| 4319 | 2010-08-23 11:03:56 |
| 7159 | 2010-08-23 11:03:56 |
| 10610 | 2010-08-23 11:03:46 |
| 5540 | 2010-08-23 11:03:46 |
| 1 | 2010-08-23 11:03:46 |
| 11942 | 2010-08-23 11:03:35 |
| 7900 | 2010-08-23 11:03:25 |
+-------------+---------------------+
如果我要手动编写这个算法,我会在(access_date
,creative_id
)上构建一个b树。我从MAX(access_date)
开始,继续走树,直到找到20个唯一的creative_ids
,然后按照我发现它的顺序返回。
使用该算法,我只需要考虑94行(其中有94行access_date >= 2010-08-23 11:03:25
,这是我们的第20大access_date
,如上所示。)
但是,MySQL在回答此查询时决定使用creative_url_index
,我不明白。执行此操作时,它会考虑超过10,000行。
ANALYZE TABLE scrape_search_results;
SELECT ...;
+----+-------------+--------------------+-------+---------------+--------------------+---------+------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------------+-------+---------------+--------------------+---------+------+-------+---------------------------------+
| 1 | SIMPLE | ScrapeSearchResult | index | NULL | creative_url_index | 8 | NULL | 10687 | Using temporary; Using filesort |
+----+-------------+--------------------+-------+---------------+--------------------+---------+------+-------+---------------------------------+
我在派生列MAX(access_date)
上执行ORDER BY是我的麻烦吗?如果是这样,我如何优化我的查询以更符合我的期望?
答案 0 :(得分:4)
我有一段时间没有在MySQL中做过这种事情(很久以来切换到PostgtreSQL)但通常我会用同心选择来处理这个问题,以欺骗查询计划员给出一个好的计划。
SELECT * FROM
(SELECT `ScrapeSearchResult`.`creative_id`,
MAX(`ScrapeSearchResult`.`access_date`) AS `latest_access_date`
FROM `scrape_search_results` AS `ScrapeSearchResult`
WHERE 1 = 1
GROUP BY `ScrapeSearchResult`.`creative_id`
) as inner
ORDER BY `latest_access_date` DESC
LIMIT 20;
这种成功完全取决于内部总行数的合理数量。
我只是查找MySQL 5.6的文档,看起来看起来应该工作......甚至在MySQL中;)