mysql order by 与 limit 混用陷阱

在Mysql中咱们经常用order by来进行排序,使用limit来进行分页,当须要先排序后分页时咱们每每使用相似的写法select * from 表名 order by 排序字段 limt M,N。可是这种写法却隐藏着较深的使用陷阱。在排序字段有数据重复的状况下,会很容易出现排序结果与预期不一致的问题。html

好比如今有一张user表,表结构及数据以下:mysql

如今想根据建立时间升序查询user表,而且分页查询,每页2条,那很容易写出sql为:select * from user order by create_time limit pageNo,2;sql

在执行查询过程当中会发现: 
一、查询第一页数据时:测试

 

二、查询第四页数据时:优化

 

 

user表共有8条数据,有4页数据,可是实际查询过程当中第一页与第四页居然出现了相同的数据。url

这是什么状况?难道上面的分页SQL不是先将两个表关联查询出来,而后再排好序,再取对应分页的数据吗???.net

上面的实际执行结果已经证实现实与想像每每是有差距的,实际SQL执行时并非按照上述方式执行的。这里实际上是Mysql会对Limit作优化,具体优化方式见官方文档:https://dev.mysql.com/doc/refman/5.7/en/limit-optimization.html 
这个是5.7版本的说明,提取几个问题直接相关的点作下说明。code

上面官方文档里面有提到若是你将Limit row_count与order by混用,mysql会找到排序的row_count行后立马返回,而不是排序整个查询结果再返回。若是是经过索引排序,会很是快;若是是文件排序,全部匹配查询的行(不带Limit的)都会被选中,被选中的大多数或者所有会被排序,直到limit要求的row_count被找到了。若是limit要求的row_count行一旦被找到,Mysql就不会排序结果集中剩余的行了。htm

这里咱们查看下对应SQL的执行计划:blog

能够确认是用的文件排序,表确实也没有加额外的索引。因此咱们能够肯定这个SQL执行时是会找到limit要求的行后立马返回查询结果的。

不过就算它立马返回,为何分页会不许呢?

官方文档里面作了以下说明: 

 

 

若是order by的字段有多个行都有相同的值,mysql是会随机的顺序返回查询结果的,具体依赖对应的执行计划。也就是说若是排序的列是无序的,那么排序的结果行的顺序也是不肯定的。

基于这个咱们就基本知道为何分页会不许了,由于咱们排序的字段是create_time,正好又有几个相同的值的行,在实际执行时返回结果对应的行的顺序是不肯定的。对应上面的状况,第一页返回的name为8的数据行,可能正好排在前面,而第四页查询时name为8的数据行正好排在后面,因此第四页又出现了。

那这种状况应该怎么解决呢?

官方给出了解决方案:

若是想在Limit存在或不存在的状况下,都保证排序结果相同,能够额外加一个排序条件。例如id字段是惟一的,能够考虑在排序字段中额外加个id排序去确保顺序稳定。

因此上面的状况下能够在SQL再添加个排序字段,好比fund_flow的id字段,这样分页的问题就解决了。修改后的SQL能够像下面这样: 
SELECT * FROM user ORDER BY create_time,id LIMIT 6,2;

再次测试问题解决!!

相关文章
相关标签/搜索