1. limit和order by 联合使用时的小陷阱
问题:order by 带limit 和不带limit 查询出来结果顺序不一样?
图一: 按照from_date排序查询dept_manager(不带limit)
图二:按照from_date排序查询dept_manager(带limit)
原因:查询mysql官方文档,得知:
1. 如果将Limit 与order by混用,mysql会找到排序的row_count行后立马返回,而不是排序整个查询结果再返回。If you combine LIMIT row_count with ORDER BY, MySQL stops sorting as soon as it has found the first row_count rows of the sorted result, rather than sorting the entire result.
2. 如果order by的字段有多个行都有相同的值,mysql是会随机的顺序返回查询结果的,具体依赖对应的执行计划。If multiple rows have identical values in the ORDER BY columns, the server is free to return those rows in any order, and may do so differently depending on the overall execution plan.
解决方法:
基于上述原因我们知道如果按照有多个相同值的列来对数据进行带limit排序,所得的结果会与真实数据不符合。如果能找到唯一确定行数剧的组合列,然后按照该组合列进行排序,问题也就会迎刃而解。
从图一中,我们可以看出,emp_no可以唯一确定某一行,如果我们按照 "from_date" 和 "emp_no" 的组合列来进行order by,就能解决上述问题。
图三:使用组合列进行排序(不带limit)
图四:使用组合列进行排序(带limit)
2. limit, order by 时使用索引优化
我们先来看不带索引的查询
图五:不加索引的order by
可以看出,order by列不是索引时,需要对全表进行扫描(扫描行数为299113);如果我们给employees表加上联合索引(hire_date, emp_no):
图六:加索引后的order by
可以看到给order by列加上索引后,扫描行数明显减少
3. 使用延迟关联来加快limit,order by
延迟关联,就是先将符合order by,limit条件的primary key列选取出来,由于选取出这些primary key 列,使用的是覆盖索引,所以速度非常快;然后再将得到的primary key 列与原表join,使用索引查询,就能快速的取出符合条件的数据:
图七:延迟关联和非延迟关联的时间对比
从上图中,我们明显可以看到延迟关联查询的效率明显高于非延迟关联
4. 建索引的一个意外收获
mysql 中二级索引会关联 primary key,所以在上述优化中,以 hire_date 创建了一个索引,就相当于创建了(hire_date, emp_no)的联合索引(employees 表中,emp_no是 primary key)。下面通过几张图,我们来验证一下:
1. 首先我们去掉 "hire_date" 索引,可以看到,order by limit使用了全表扫描
图八:不带索引的order by limit
2. 接下来,我们给 employee 表添加(hire_date, emp_no)索引, 可以看到扫描行数减少到80行,索引起作用了
图九:索引是(hire_date, emp_no)的order by limit
3. 最后,我们用(hire_date)索引取代 (hire_date, emp_no),然后发现,效果和(hire_date, emp_no)索引一致。
图十: 索引为(hire_date)的order by limit