前言
之前我踩过一个order by + limit的坑,最近发现有另外一个同事也踩坑了,现在拿出来跟大家分享一下。
1 背景
我之前提供过一个品牌的分页查询接口,该接口支持按品牌名称模糊搜索。
由于品牌的数据很多,用户输入关键字之后,有可能搜索出很多满足条件的品牌数据。
比如输入关键字:苏三,可能搜索出这样的数据:
苏三说技术
苏三说
我是苏三
苏三的知识星球
苏三
苏三的公众号
球主苏三
...
产品提了一个需求,把匹配度最高的显示到前面。
为了实现产品的需求,我使用了char_length()函数。
sql改成类似这样的:
select * from brand
where name like '%苏三%'
order by char_length(name)
limit 0,20增加了按名称长度升序的功能,这样可以保证返回的数据是这样的:
苏三
苏三说
我是苏三
球主苏三
苏三说技术
苏三的公众号
苏三的知识星球
...
匹配度最高的排到最前面。
但也却带来了另外一个问题,分页出现了重复的数据,第一页有某某数据,第二页也有某某数据。
为什么会出现这么神奇的现象呢?
2 排序
在mysql中order by关键字是给sql语句排序。
如果你不加order by,mysql默认按主键排序。
但如果你加了order by,同时后面的排序字段出现了重复的数据,mysql对相同的数据的排序是随机的。
order by char_length(name) 使用品牌名称长度升序,而品牌名称的长度有可能出现相同的情况,比如:我是苏三和球主苏三。
如果没有用limit做分页,一次性查询所有的数据,数据只是顺序稍微有点不一样,返回的记录条数和结果没有问题。
但如果这时用limit做了分页,就可能会出现问题。 我是苏三,有可能在第一次查询的时候,在第一页出现了,在第二次查询的时候,却出现在了第二页。
那么,如何解决问题呢?
3 如何解决问题
上面的order by + limit 出现了数据重复的问题。
其根本原因是order by后面的字段,出现了重复的数据。示例中使用了char_length(name),出现了相同长度的品牌名称。
有没有办法将排序结果固定呢?
答:可以增加排序字段。
我们可以在order by后面增加id,即主键排序。
select * from brand
where name like '%苏三%'
order by char_length(name),id
limit 0,20先按品牌名称长度排序,如果名称长度相同,再按主键排序。
这样可以保证每次order by的排序结果是一样的,不会出现随机排序的情况。
order by + limit的问题,我们在实际工作中,遇到的概率很大,大家一定要引起足够的重视。