mysql - 如何从我的 SQL 查询中删除临时文件和文件排序?

标签 mysql sql optimization indexing query-optimization

我一直在尝试在 MySQL 中创建索引,但每当我对查询运行解释时,都会不断获取临时索引和文件排序。

我的表格的简化版本如下所示:

ordered_products
    op_id INT UNSIGNED NOT NULL AUTO_INCREMENT
    op_orderid INT UNSIGNED NOT NULL
    op_orderdate TIMESTAMP NOT NULL
    op_productid INT UNSIGNED NOT NULL

products
    p_id INT UNSIGNED NOT NULL AUTO_INCREMENT
    p_productname VARCHAR(128) NOT NULL
    p_enabled TINYINT NOT NULL

'ordered_products' 表目前有超过 1,000,000 行,记录了所有已订购的产品,以及它们所属的订单。该表增长迅速。

“产品”表目前有大约 3,000 行,包含待售产品列表。

该站点显示给定时间段(通常是最近 3 天)的热门产品列表,我的查询如下所示:

SELECT COUNT(op.op_productid) AS ProductCount, op.op_productid
FROM ordered_products op
LEFT JOIN products p ON op.op_productid=p.p_id
WHERE op.op_orderdate>='2014-03-08 00:00:00'
AND p.p_enabled=1
GROUP BY op.op_productid
ORDER BY ProductCount DESC, p.p_productname ASC

当我运行该查询时,它通常需要大约 800 毫秒(0.8 秒)才能执行,这很荒谬。我们已经通过缓存解决了这个问题,但是每当缓存过期时,我们的速度就会变慢。我需要解决这个问题。

我尝试过对表进行索引,但无论我尝试什么,我都无法避免临时和文件排序。 EXPLAIN 的输出是:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  p   index   PRIMARY,idx_enabled_id_name idx_enabled_id_name 782 \N  1477    Using where; Using index; Using temporary; Using filesort
1   SIMPLE  op  ref idx_pid_oid_date    idx_pid_oid_date    4   test_store.p.p_id   9   Using where; Using index

如果我删除 GROUP BY,文件排序就会消失,但我需要它来确保 ProductCount 值显示每个产品计数而不是所有产品的总和。

如果我删除 GROUP BY 和 ORDER BY ProductCount,临时和文件排序都会消失,但现在我留下了一个非常糟糕的结果集。

谁能帮我解决这个问题?我试过很多不同的索引,也试过无数次重写SQL,但都没有成功。

如有任何帮助,我们将不胜感激。

最佳答案

在计算列 ProductCount 上使用 ORDER BY 时,您无法摆脱临时表和文件排序。计算列没有索引,因此它必须在查询时进行排序。

我尝试通过实验重现您的结果。我可以在 op_productid 上放置一个索引,然后优化器可能会使用它来执行 GROUP BY

mysql> EXPLAIN SELECT COUNT(op.op_productid) AS ProductCount, op.op_productid 
FROM ordered_products op FORCE INDEX (op_productid) STRAIGHT_JOIN products p 
  ON op.op_productid=p.p_id 
WHERE op.op_orderdate>='2014-03-08 00:00:00' AND p.p_enabled=1 
GROUP BY op.op_productid ORDER BY null;

在我的例子中,我不得不使用 STRAIGHT_JOIN 和 FORCE INDEX 来覆盖优化器。但这可能是由于我的测试环境造成的,我在每个表中只有 1 或 2 行进行测试,这会影响优化器的选择。在你的真实数据中,它可能会做出更明智的选择。

此外,如果 WHERE 子句中的条件使联接隐式成为内部联接,则不要使用 LEFT JOIN。了解联接的类型及其工作原理——不要总是默认使用 LEFT JOIN。

+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key          | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+
|  1 | SIMPLE      | op    | index | op_productid  | op_productid | 4       | NULL |    5 | Using where |
|  1 | SIMPLE      | p     | ALL   | PRIMARY       | NULL         | NULL    | NULL |    1 | Using where |
+----+-------------+-------+-------+---------------+--------------+---------+------+------+-------------+

你唯一的选择是存储一个非规范化的表,其中计数是持久的。然后,如果您的缓存失败,刷新缓存的查询就不是一个昂贵的查询。

关于mysql - 如何从我的 SQL 查询中删除临时文件和文件排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22925918/

相关文章:

PHP/MYSQL - bool 全文搜索 - 精确字符串运算符 ("")在准备语句与 SQL 查询中给出不同的结果集

mysql - 如何优化 JOIN 以加快查询速度?

mysql - 如何清理 C 中的 SQL 查询?

mysql - 如何使用考虑两列的 GROUP BY?

angular - 使用 Angular CLI 预加载字体

http - 如何压缩 HTTP 响应头?

mysql - 如何在一列中显示两列不同类型的内容?

sql - Postgres : Foreign key doesn't match all rows

SQL Server 2000奇怪的子查询问题

database - 你怎么不加入?