sql - 如何优化在 MySQL 的派生列上执行 ORDER BY 的查询?

标签 sql database mysql

我在优化涉及 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 可能出现多次(数百次)。我要回答的问题是一个相对简单的问题;给我按 access_date 排序的前 20 个 creative_ids。这是我的 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 行(access_date >= 2010-08-23 11:03:25 有 94 行,这是我们第 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 有问题吗?如果是这样,我如何优化我的查询以更符合我的期望?

最佳答案

我已经有一段时间没有在 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 中也是如此 ;)

关于sql - 如何优化在 MySQL 的派生列上执行 ORDER BY 的查询?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3561990/

相关文章:

php - 在 PHP 中使用数据库创建类别

sql - 将子表连接到主表

mysql - '/' 应用程序 VB.NET 中的服务器错误

php - 从文本框提交 MySQL 查询?

Android 将数据库从内部存储复制到外部存储

php - 如何在 PHP 中插入 NULL 值

java - 为不同的员工更新 managerId

php - SQL 代码点火器 : Create savepoint and roll-back to the savepoint/Rollback multiple transaction from controller

php - 我的 IF 语句有问题

mysql - 获取错误到 : MySQL stored procedure with parameters in Access frontend via VBA