如果使用 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_2StatausID, 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 子句中

如何使用Order By和With子句

JAVA8中对List<map<String,Object>>根据map某个value值进行排序,还支持中文排序,可以替代order by

EF - order by specific values

ElasticSearch Aggregation Group by order by sub terms field doc count