MySQL模糊查询/模式匹配(Pattern Match)

      使用SQL查询数据时,时常会遇到这种情况,我们并不需要精确的匹配,而是要查找具有某类特点的数据。这种场景我们就要用到模糊查询。MySQL中常用的模糊查询方法有2种:

  • like语句模糊查询
  • regexp正则表达式模式匹配

目录

一、使用like模糊匹配

二、使用正则表达式模式匹配

2.1 普通匹配

2.2 位置匹配

2.3 特定次数匹配

2.4 特定模式匹配

2.5 修改匹配的数据


、使用like模糊匹配

当只需要进行简单模糊查询时,可以利用like语句完成,like可以用来模糊匹配字符串。

like语句的语法是:expr [not] like 'pattern'。expr可以是我们的数据列或者某种表达式,pattern就是我们想要查询的数据具有的模式。在'pattern'中,可以使用'_'来匹配单个字符或'%'来匹配任意字符串(包含空字符,但不会匹配null)。

下面用一个示例来演示like用法,先建立一张测试表:

create table test(
id int not null primary key,
value varchar(32));

insert into test values(1,'abc'),(2,'abcd'),(3,'abc123'),(4,'ab123xyz');

查询和abc有关的数据:

select * from test;

select * from test where value like 'abc';

  • 当模式中没有_或%时,like会只用精确匹配(相当于=),这里只有abc了被匹配出来。

使用_匹配单个字符(包含空字符,但不会匹配null):

select * from test where value like 'abc_';

这里的模式是'abc_',使用_匹配单个字符,只有abcd被匹配了出来。

使用%匹配任意字符:

select * from test where value like 'abc%';

当使用%,匹配包含空字符在内的所有字符串。

使用not like反向匹配,即所有符合模式的都不查询出来:

select * from test where value not like 'abc%';

这里所有以abc开头的字符串都不会显示出来。

上面的例子都是以查找以abc开头的示例,_和%可以出现在模式的任何地方。需要注意的一点是,如果匹配的字段上有索引,如果遇到'%str'这种将%放在模式开头,那么将会导致索引失效,使用中需要斟酌一下性能。

二、使用正则表达式模式匹配

like 匹配的方式可以完成一些简单的模糊查询,例如你可以用%abc%来匹配任意包含abc的数据,但是如果问题换成:包含a或b或c,那么你就要写3次匹配,如果问题更复杂一点(例如匹配特定次数),like可能就无法完成了,此时你就需要采用正则表达式匹配。

正则表达式是一个包含文本和特殊字符的字符串,利用它可以识别各种复杂模式的字符串。MySQL可以通过regexp或rlike(这两个是同义词)操作符来完成正则表达式的匹配,语法和like一样 expr [not] regexp 'pattern'。

正则表达式的匹配结果,如果成功则返回1,失败返回0,用在where条件中分别对应真/假:

select 'abcd' regexp 'abc';

select 'abcd' regexp '123';

下面演示几类最常用的正则表示式匹配场景,我们先将测试数据换成更复杂内容:

truncate table test;

insert into test values(1,'abc'),(2,'abcd'),(3,'abc 1'),(4,'abc1,xkz'),(5,'abc13'),(6,'xyzABC123'),(7,'xyzabc1223'),(8,'123456'),(9,'1212123xyzabc');

commit;

测试数据如下:

select * from test;

2.1 普通匹配

普通匹配和like类似,直接输入字符串,则会匹配出包含改字符串的值:

select * from test where value regexp 'abc';

  • regexp 'abc' 相当于like '%abc%',所有包含abc的都匹配的出来,除了id为8的纯数字。

反向匹配,可以直接在regexp 前加上not,或者整体取反:

select * from test where value not regexp 'abc';

select * from test where not(value regexp 'abc');

  • expr not regexp … 和 not (epxr regexp …)两种写法是等效的

2.2 位置匹配

某些时候我们需要从特定的位置开始匹配,正则表达式可以通过^和$来指定匹配的位置:

  • ^ 代表从开头进行匹配
  • $ 代表从结尾进行匹配

匹配以'xyz'开头的数据

select * from test where value regexp '^xyz';

匹配以'23'结尾的数据

  • (23)的小括号表示这是一个整体

2.3 特定次数匹配

有些时候我们想知道某种模式出现的次数,这时我们就需要用到次数匹配了,常用的次数匹配模式如下:

  • […]   匹配方括号出现的任意字符
  • .       匹配任意单个字符
  • *      匹配前面模式0次或多次
  • +     匹配前面模式1次或多次
  • ?    匹配前面模式0次或1次
  • {m}  匹配前面模式m次
  • {m,n} 匹配前面模式m到n次

匹配包含字符串'k'或者'5'的数据:

select * from test where value regexp '[k5]';

匹配x和z中间包含一个字符的模式:

select * from test where value regexp 'x.z';

匹配以abc开头,后面跟任意字符

select * from test where value regexp '^abc.*';

  • .* 代表任意字符出现任意次数,可以匹配任何字符,这里去掉效果也是一样的

匹配abc1之后,出现1次或多次2的数据:

  1. select * from test where value regexp 'abc12+';

  • abc12+,+代表前面的2出现1次或多次

匹配abc1之后,出现0次或1次2的数据:

select * from test where value regexp 'abc12?';

  • 注意和上个例子区别,?代表0次或1次,因此只要含abc1就会匹配出来
  • id为7的数据,结尾1223,满足了出现1次2,所以也被查了出来

查询abc1之后,2出现2次的数据:

select * from test where value regexp 'abc12{2}';

  • 只要2出现2次即视为满足条件,如果出现更多次数的2,依然是满足的(并不管后续数据,只要匹配到2次就判定成功)。

查询包含6个数据的数据:

select * from test where value regexp '[0-9]{6}';

  • [0-9]代表数字1到9任意数字,{6}代表重复6次,只要有6个连续的数字即满足条件。

查询仅包含6个数字的数据:

select * from test where value regexp '^[0-9]{6}$';

  • 我们把上个模式用^和$限制起来,代表从开头到结尾,只有6数字,id为9的数据(数字后包含字符串)就不匹配了。

查询'12'重复1到2次的数据:

select * from test where value regexp 'abc(12){1,2}';

  • (12)用括号括起来代表一个整体
  • {1,2} 代表匹配1次或2次,只要满足即可以。

2.4 特定模式匹配

MySQL还提供了一些特殊字符串来表示某一类字符,其格式是[:character_class:],常用的类别有:

  • [:digit:] 表示所有数字
  • [:alpha:] 表示所有字符
  • [:alnum:] 表示所有数字和字符
  • [:blank:] 表示空白字符
  • [:punct:] 表示标点符号
  • [:upper:] 表示大写字符
  • [:lower:] 表示小写字符

查询所有以数字开头的记录:

select * from test where value regexp '^[:digit:]';

  • [:digit:] 代表所有数字,前面的^代表从开头开始匹配

查询所有以字母结尾的记录:

select * from test where value regexp '[:alpha:]$';

查询所有包含数字或字母的记录:

select * from test where value regexp '[:alnum:]';

查询所有包含空格的记录:

select * from test where value regexp '[:blank:]';

查询所有包含标点符号的记录:

select * from test where value regexp '[:punct:]';

查询包含大写字母的记录:

select * from test where value regexp '[:upper:]' COLLATE utf8mb4_0900_as_cs;

  • 这里多了一个子句COLLATE utf8mb4_0900_as_cs,指定排序规则区分大小写。MySQL8.0中字符集utf8mb4的默认排序规则是utf8mb4_0900_ai_ci,它是不区分大小写的(排序规则结尾的ci代表Case Insensitive 大小写不敏感,cs代表Case Sensitive 大小写敏感)。

如果不加这个排序规则子句,默认是不区分大小写的,纯小写字符记录也会被查询出来,与我们想查大写字符的意愿不同:

select * from test where value regexp '[:upper:]';

你也可以使用函数regexp_like函数中的参数来指定大小写是否敏感(这个函数是regexp的另一种写法):

select * from test where regexp_like(value, '[:upper:]', 'c')=1;

  • 前两个参数是expr和pattern,第三个参数可以指定大小写是否敏感:'c' 大小写敏感,'i' 大小写不敏感

2.5 修改匹配的数据

很多时候,我们查找出某类数据后,就是为了修改它,利用regexp_replace函数可以修改正则表达式的匹配结果。

语法:regexp_replace(expr, pattern, replacement [, pos[, occurrence[, match_type]]])

参数解释:

  • expr: 要搜索的内容
  • pattern: 匹配的模式
  • replacement: 替换的内容
  • pos: 起始搜索的位置,默认是1,即从头开始匹配
  • occurrence: 匹配发生的次数,默认是0,即替换所有匹配项
  • match_type: 匹配模式,和regexp_like相同,常用的是:'c' 大小写敏感,'i' 大小写不敏感

下面通过几个示例来理解,将字符串'ababab'中的a替换为x:

select regexp_replace('ababab','a','x');

将字符串'ababab'中的a替换为x,但是从第2位开始搜索:

select regexp_replace('ababab','a','x',2);

  • 这里使用了一个位置参数2,表示从第二位开始搜索,所以开头的a没有被替换

将字符串'ababab'中的a替换为x,但是从第2位开始搜索,并将第2次匹配的位置替换为x:

select regexp_replace('abababab','a','x',2,2);

  • 这里使用了位置参数2,和发生的次数2,即从第2位开始,只有第二个匹配的a会被替换为x

将test表中的首次匹配2大写字符替换为字符串'这里有两个大写字符':

select value, regexp_replace(value,'[:upper:]{2}','这里有两个大写字符',1,1,'c') after_replace from test where value regexp '[:upper:]' COLLATE utf8mb4_0900_as_cs;

  • 模式[:upper:]{2},代表任意大写字符重复2次
  • 后面的1,1分别代表从头开始搜索,只匹配第一次
  • 'c' 代表大小写敏感

修改前要先查询一下元数据和修改后的数据(最好备份一下),一是确认修改范围是不是我们想要匹配的数据,二是修改结果对不对,没有问题时再执行update:

update test set value=regexp_replace(value,'[:upper:]{2}','这里有两个大写字符',1,1,'c') where value regexp '[:upper:]' COLLATE utf8mb4_0900_as_cs;

select * from test;

可以看到,我们通过正则表达式匹配并更新成功了。

注意:在MySQL8.0.17版本前,这个函数返回的结果字符集是UTF-16(这是一个BUG),这可能导致你查询的结果和最后的更新结果不同,因此采用此函数批量匹配更新前一定要做好备份。

以上便是MySQL中常用的模糊匹配方法,可以满足大部分场景的模糊匹配。但是如果数据量非常大,例如需要在富文本中匹配想要的数据,上面的匹配方法有可能会比较慢,此时可以考虑采用使用另一种匹配技术:全文索引(Full-Text Index)

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值