如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行
Posted
技术标签:
【中文标题】如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行【英文标题】:MySQL query takes long to execute if using ORDER BY String Column 【发布时间】:2014-10-09 21:31:01 【问题描述】:所以如果我不使用order by
,我对包含4 million
记录的表的查询会立即执行。但是,我想为我的客户提供一种按Name
字段对结果进行排序的方法,并且只显示过滤结果的最后一个100
。我添加 order by Name
后,需要 100 秒才能执行。
我的表结构是这样的:
CREATE TABLE Test(
ID INT PRIMARY KEY AUTO_INCREMENT,
Name VARCHAR(100),
StatusID INT,
KEY (StatusID), <-- Index on StatusID
KEY (StatusID, Name) <-- Index on StatusID, Name
KEY(Name) <-- Index on Name
);
我的查询只是执行以下操作:
explain SELECT ID, StatusID, Name
FROM Test
WHERE StatusID = 113
ORDER BY Name DESC
LIMIT 0, 100
当我通过Name
订购时,上面的解释给出了这个结果:
StatusID_2
是StatausID, Name
的复合索引
现在,如果我将 ORDER BY Name DESC
更改为 ORDER BY ID
,我会得到:
我怎样才能使它在使用 ORDER BY Name
时也只检查 100
行?
【问题讨论】:
您是否对查询运行EXPLAIN
以查看查询计划有何不同?
@cdhowie 更新了我的答案。按顺序查看 380 万行,我知道问题所在,但我该如何解决。我仍然需要订购它。
确实很奇怪,我希望它使用Name
上的索引。如果您在加入之前进行排序和限制会发生什么? (SELECT * FROM (SELECT * FROM Test WHERE StatusID = 12 ORDER BY Name DESC LIMIT 0, 100) t JOIN ...
,并在连接子句中使用t
而不是Test
。)
@cdhowie 我尝试了类似的方法,但结果相同。
那么EXPLAIN
在哪里? PS:很明显,Name
上的专用索引无济于事,不是吗?很明显,“加入不是问题”的说法是错误的。
【参考方案1】:
您可以尝试一件事,尝试在结果中预期在 100 行中的字母,例如
SELECT *
FROM Test
*** Some Joins to filter data or get more columns from other tables
WHERE StatusID = 12 AND NAME REGEXP '^[A-H]'
ORDER BY Name DESC
LIMIT 0, 100
此外,使用索引对名称非常重要(已应用)——在这种情况下,将启动索引范围扫描,并在生成所需数量的行后立即停止查询执行。
所以我们不能无缘无故地使用 ID,因为当它达到其限制时它不会扫描,我们唯一可以尝试的是删除在预期结果中不可能的字母,这就是我们正在尝试做的事情使用正则表达式
【讨论】:
更新了我的问题请看。【参考方案2】:没有连接和解释结果很难说,但你并没有明显地使用索引。
这可能是因为连接或因为您在 where 子句中有另一个键。我建议阅读这篇文章,它涵盖了所有可能的情况:http://dev.mysql.com/doc/refman/5.7/en/order-by-optimization.html
增加sort_buffer_size
和/或read_rnd_buffer_size
可能会有所帮助...
【讨论】:
你可以试试这个:select id,statusid,name from test use index(name) where statusid=5 order by name limit 5;
【参考方案3】:
您需要一个基于过滤 WHERE 条件加上排序依据的复合键...创建索引
(状态ID,名称)
这样,WHERE 会直接跳转到您的 StatusID = 12 条记录并忽略其余 400 万条记录...然后使用名称作为辅助来限定 ORDER BY。
没有看到其他表/连接条件和关联索引,您可能还想尝试添加 MySQL 关键字
SELECT STRAIGHT_JOIN ...查询的其余部分
因此它会按照您选择的顺序进行查询,但如果没有看到前面提到的其他联接,则不确定影响。
添加(根据反馈)
我只会删除 ID 上的各个索引,这样引擎就不必猜测要使用哪个索引。无论名称如何,复合索引都可以用作仅 ID 查询,因此您不需要同时拥有两者。
此外,删除仅名称索引,除非您将主要在名称上查询作为没有 ID 限定符的 where 限定符...此外,您查询的示例 ID 甚至可能有多少条总记录400 万...您可能希望将 id 的完整集作为子查询提取,获得几千并按名称排序,这会很快...类似于...
select *
from ( SELECT
ID,
StatusID,
Name
FROM
Test
WHERE
StatusID = 113 ) PreQuery
ORDER BY
Name DESC
LIMIT 0, 100
【讨论】:
一个重要说明:这个答案是基于mysql没有重新排序连接的假设 @DRapp 更新了我的问题,请看一下。 @DRapp 我这样做了,现在它检查了 380 万行 * 2。一个用于派生表。 :( @GGio,除非你有 380 万行的状态 = 113 @GGio,在您的原始查询中,您提到了其他表/联接/标准,您的查询几乎立即返回。这些其他标准中的一些是否可以比您正在寻找的更小,这成为最终查询的基础?使用该数据编辑您的帖子可能有助于其他途径。以上是关于如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行的主要内容,如果未能解决你的问题,请参考以下文章
如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行
SQL 错误 - Windows 函数只能出现在 SELECT 或 ORDER BY 子句中
JAVA8中对List<map<String,Object>>根据map某个value值进行排序,还支持中文排序,可以替代order by
ElasticSearch Aggregation Group by order by sub terms field doc count