페이징 + 퍼지 쿼리에는 구덩이가 있습니다!

머리말

쿼리 문을 사용 Mysql했는지 모르겠습니다 .like模糊

分页쿼리 결과를 처리 했는지 모르겠습니다 .

퍼지 쿼리와 페이징 처리, 예상치 못한 구덩이가 있을 것입니다.

프론트 엔드 브랜드 선택 제어에 사용되는 브랜드 쿼리 인터페이스를 이전에 제공한 적이 있습니다.

그 당시에는 성능상의 이유로 한 번에 너무 많은 브랜드를 로드하여 프런트 엔드 컨트롤로 인해 페이지가 정지될까 두려웠습니다.

따라서 브랜드 쿼리 인터페이스가 分页처리됩니다.

초기에는 브랜드 테이블의 데이터가 상대적으로 작아 문제가 없었다.

나중에 제품에 수요가 추가되었고 브랜드 드롭다운 선택 컨트롤에서 사용자가 사용자 지정 브랜드를 입력할 수 있습니다.

사용자가 브랜드를 추가하기 전에 먼저 확인해야 하며, 해당 브랜드가 있으면 기존 브랜드를 사용합니다. 존재하지 않는 경우 새 브랜드를 추가하십시오. (여기서 정확히 일치)

이 요구 사항은 간단하고 구현하기 쉽습니다.

나중에 제품이 다시 수요가 模糊查询생겨 이름을 브랜드화해야 했습니다.

기능이 출시된 후 오랫동안 문제 없이 사용되었습니다.

어느날 갑자기 이 기능에 문제가 생겼습니다.

정말 무슨 일이?

1. 범죄현장

어느 날 오후, 작업은 다음과 같은 테스트 피드백을 발견했습니다. 분명히 Susan 브랜드는 이미 존재하지만 苏三사용자가 키워드:를 입력하면 시스템에서 사용자가 기존 브랜드를 직접 선택하도록 하지 않고 Susan 브랜드라는 사용자 지정을 추가합니다.

보러 갔는데 정말 문제가 있습니다.

잠시 후 원인을 찾았고 1차 판단은 페이징 문제였다.

검색 키워드: Su San, 데이터 페이지가 여러 개 있는데 브랜드 테이블에 데이터가 왜 이렇게 많은지 깜짝 놀랐습니다.

데이터베이스를 확인해보니 사실 데이터 양이 많지는 않지만 일부 브랜드 이름은 특별하고 일부 브랜드 이름은 Su San, Li Si 또는 Su San, Li Si와 같은 여러 브랜드 이름으로 구성되어 있습니다. , 왕 우, 이것은 브랜드입니다.

사실 문제는 브랜드 이름의 비표준 구성으로 인해 발생하지만 더 이상 브랜드를 수정하는 작업이 불가능하며 현재 문제는 기술적 인 수단을 통해서만 해결할 수 있습니다.

첫 번째 페이지의 데이터 sql을 쿼리합니다.

select * from brand where name like '%苏三%' 
order by edit_date desc limit 5;
复制代码

실행 결과: 그림에서 이 두 단어에 해당하는 데이터가 없음을 알 수 있습니다 .苏三

注意:为了好演示,这里给的每页大小是5,真实的场景并非如此。

查询第二页的数据sql:

select * from brand where name like '%苏三%' 
order by edit_date desc limit 5,5;
复制代码

执行结果: 从图中看到,在第二行,出现了正好等于苏三这两个字的数据。

用户搜索关键字:苏三 时,前端页面在调用品牌查询接口,pageNo默认是1。由于能够匹配关键字的数据太多了,第一页返回不完,需要多页才能全部返回。

前端获取到第一页的数据后,跟关键字:苏三 做比较,发现没有等于苏三的品牌。

这样就会在下拉控件中,自动添加一个品牌:苏三,同时在右边增加自定义标签

这样就出问题了,明明苏三这个品牌是有的,但用户还能自定义一个苏三,而不是直接选择。

2.思考123

苏三这个关键字,通过模糊查询可以查询出来,但由于品牌接口做了分页,全匹配的品牌:苏三,出现在第二页了,才导致问题的产生。

如果要解决这个问题,让它出现在第一页不就OK了?

这时候,就有下面几种解决方案。

2.1 方案1

分页查询品牌接口,pageSize是5。

我们为什么不把pageSize调大一点?比如改成:200、500等。

这样通过苏三关键字,进行模糊查询的时候,结果基本都在第一页。

这样就能非常快速的解决问题。

但有个缺点就是:如果这次调大了pageSize,但后面查询关键字的品牌又出现在第二页怎么办?

不可能一直改pageSize吧?

2.2 方案2

把分页查询接口的数据,拆分成两部分:

  1. 精确查询
  2. 模糊查询

在代码中做处理的时候,先根据关键字精确查询,即sql中使用name='苏三',这种方式查询一次数据。

如果没查出数据,则再直接用like '%苏三'进行模糊查询。

如果查出了一条数据,则把它放在返回结果集合中的第一位置。接下来,使用like '%苏三'进行模糊查询的时候,再加上条件 name <> '苏三'。将查出的结果,从第二个位置往后放。

这样可以拼接出你想要的集合。

但有个缺点,就是代码耦合性太大了。

2.3 方案3

之前,品牌苏三在第二页,最根本的原因是使用了edit_time字段进行逆序的。

也就是说,修改时间越大的越排在前面,而品牌苏三的修改时间很小,所以排在第二页了。

如果想品牌苏三,排在第一页,修改一下排序规则,不就搞定了?

可以改成按:id或者name字段排序。

用id字段排序,不太合适,虽说用了雪花算法,但跟修改时间类似,先插入的数据,会越小。

select * from brand where name like '%苏三%' 
order by id desc limit 5;
复制代码

用它排序的结果,跟使用修改时间排序差不了太多。 看来,只能使用name字段排序了。

3.如何排序?

我们在sql中直接对name字段,进行升序或者降序吗?

显然不是。

使用name字段降序

select * from brand where name like '%苏三%' 
order by name desc limit 5;
复制代码

执行结果: 图中并没有看到我们想要的数据。

其实,使用name字段升序,也可能在第一页查不出我们所想要的数据。

到底该如何处理呢?

假如,我们有这样一种排序:

  1. 全匹配显示在最前面,比如:苏三。
  2. 数据左半部分匹配,右边按字母排序,比如:苏三1、苏三2、苏三说技术。
  3. 从中间开始匹配,比如:1苏三、2苏三。
  4. 第2步和第3步,还要根据字符长度排序,字符短的排在前面,比如:1苏三、1苏三1、苏三说技术。

如果我们能实现上面的这种排序方式,这个问题就能完美解决了。

说起来容易,做起来难。

难道要先全匹配:name='苏三',再有匹配:name like '苏三%',再左匹配:name like '%苏三',把查询三次的结果组装起来?

显然这种做法有点low。

要实现上面我们设想的排序方式,在es中更好处理一下,但在mysql中要怎么处理呢?

4.解决方案

其实,我们可以换一种思路,根据字符的长度排序

mysql给我们提供了很多非常有用的函数,比如:char_length

通过该函数就能获取字符长度。

sql调整如下:

select * from brand where name like '%苏三%' 
order by char_length(name) asc limit 5;
复制代码

name字段使用关键字模糊查询之后,再使用char_length函数,获取name字段的字符长度,然后按长度升序

仅这一个骚操作,就搞定需求了: 我们所期待的:苏三,终于排在第一个了。同时由于该sql做了分页的,即使name字段在查询时丢失了索引,执行效率也不会太低。

业务上的需求搞定了。

但追求完美的我们,好奇,想看看第二页是什么情况:

select * from brand where name like '%苏三%' 
order by char_length(name) asc limit 5,5;
复制代码

执行结果: 并没有按照我们设想的剧本进行下去,我们之前假设的3条排序中,第2条和第3条都没有满足。

这时该怎么办?

答:可以使用mysql中的locate函数,通过它可以匹配的关键字,在字符串中的位置。

使用locate函数改造之后sql如下:

select * from brand where name like '%苏三%' 
order by char_length(name) asc, locate('苏三',name) asc limit 5,5;
复制代码

执行结果: 完美,终于出现我们想要的结果了。

除此之外,还可以使用:instrposition函数,它们的功能跟locate函数类似,在这里我就不一一介绍了,感兴趣的小伙伴可以找我私聊。

5. 总结

其实,模糊查询分页,如果分开用,一般是没问题的。

但如果它们要一起使用,一定要考虑排序问题。

如果只是按照简单的时间或者id排序,有些特殊的业务场景,没办法满足,很容易出现bug。

当然解决上面问题,还有其他办法,比如:pageSize调大一点,或者把全匹配放到第一页。

但更优的方案,是通过mysql的函数来解决问题。

我们可以通过mysql提供的:char_lengthlocateinstrposition函数等,来实现很多复杂的排序功能。

最后说一句(求关注,别白嫖我)

如果这篇文章对您有所帮助,或者有所启发的话,帮忙扫描下发二维码关注一下,您的支持是我坚持写作最大的动力。

求一键三连:点赞、转发、在看。

关注公众号:【苏三说技术】,在公众号中回复:面试、代码神器、开发手册、时间管理有超赞的粉丝福利,另外回复:加群,可以跟很多BAT大厂的前辈交流和学习。

Ich denke du magst

Origin juejin.im/post/7143077564810592287
Empfohlen
Rangfolge