最近帮产品写几个SQL脚本,分析项目的一些关键指标,统计需要利用某个字段中的值的状态进行判断,该字段结构是一个json串,关键字段用字母取代,结构如下:
{"a": [], "b": [], "c": [], "d": {"e": [], "f": 0, "g": 0}}
其中a、b、c字段都是列表,d字段是一个对象。a、b、c字段中列表项还有可能是列表,如此大概。
假设我给这个字段取名为nature,表名为test,需要判断的条件是该字段为空即符合,SQL如下:
select count(*) from test where nature is null;
看起来能满足条件,但是实际场景是,该字段由于一些历史原因在为空的场景下有两种状态值,一种是NULL,一种是类似于上面json串的形式。即便是空,d字段也会初始化几个值,所以仅用上面的SQL就很难过滤出想要的条件了,这时正则匹配就用上了。
根据上面第二种值的形式,分析出什么情况下满足该字段有值写入,分析出两点:
前三个字段(a,b,c)若有值,则结构应为[{对象1},{对象2},{对象n}]
若d字段有值,则e字段中也必定会有如上结构的值,即[{..},{..},{..}]
综上:满足这个有值场景的条件即该字段包含中包含[{字符。
注意,以上推导是根据业务场景来的,如果有需要提取字符的直接指定字符即可。
在分析完需要判断的字符后,写下SQL,如下
select count(*) from test where nature REGEXP '[{';
执行完就报了下面的错
翻译过来啥意思呢,大概就是说从正则表达式获得的错误,括号的不平衡。这是咋回事呢?"["即方括号是属于特殊字符,需要使用转义字符,知道了原因以后更改SQL。
select count(*) from test where nature REGEXP '\[\{';
执行完毕发现依旧是报了上面的错误,
正则匹配在很多编程语言中都有用,且转义字符是一个反斜杠,但是在Mysql中是使用了两个反斜杠的表示法,因为Mysql自己会利用一个转义字符来识别,剩下的一个才会交由正则库去处理。
更改上面的SQL语句。
select count(*) from test where nature REGEXP '\\[\\{';
可以发现这回执行成功了。这部分内容希望提醒大家在使用涉及到特殊字符的时候,记得要加两个斜杠,下面是Mysql中一些常见的特殊字符:
特殊字符 | 转义后 | 解释 |
---|
[ | \\[ | 匹配左方括号 |
] | \\] | 匹配右方括号 |
. | \\. | 匹配点号 |
\ | \\\ | 匹配反斜杠自己 |
只是列举几个常用的特殊字符并不全,在一些不确定的字符出错时可以想想是不是特殊字符,来定位问题。
实际到这里已经可以筛选出来nature字段值不为null的数据了,但是需求是统计该字段为空的数据(也就是包含null和刚刚利用正则过滤的这个),首先想到了查询否定,接下来试试可不可行,如下SQL:
select count(*) from test where nature REGEXP '[^\[\{]';
发现结果不如预期,比预期的数量要多很多,经过分析发现,这里需要匹配的是两个字符组合在一起的[{,而利用上面的方式,其实是等价于or的情况的,也就是匹配到[或者{都符合条件,那这数量就很多了。
Mysql除了提供REGEXP外,还提供了NOT REGEXP操作符,表示了一种不包含的意思。所以最终的SQL就是
select count(*) from test where nature not REGEXP '\[\{' or nature is null;
正则匹配功能非常强大,从实用性角度挑选几个演示。
准备一张单词表,插入一万条记录做演示数据。
// 1.建表
CREATE TABLE `words` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(64) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
// 2.存储过程
delimiter ;;
create procedure idata()
begin
declare i int;
set i=0;
while i<10000 do
insert into words(word) values(concat(char(97+(i div 1000)), char(97+(i % 1000 div 100)), char(97+(i % 100 div 10)), char(97+(i % 10))));
set i=i+1;
end while;
end;;
delimiter ;
// 3.调用
call idata();
数据源参考:如何正确地显示随机消息?
接下来,开始测试。
匹配以某一字符开头的字符串
例子:匹配以he开头的字符串
select * from words where word REGEXP '^he';
匹配以某一字符结尾的字符串
例子:匹配以d结尾的字符串
select * from words where word REGEXP 'f$';
匹配包含某一字符的字符串
用法:直接输入需要匹配的字符
例子:匹配包含ai的字符串
select * from words where word REGEXP 'ai';
匹配任意单个字符
比如有这么几个单词,boot,shoot,root,现在需要匹配包含这种形式的字符,可以使用.来匹配,那么不管前面是什么都可以。
select * from words where word REGECP '.oo';
匹配或查询
类似于执行select查询语句的or条件,理解为并入到这个正则的表达式中。
例子:匹配包含ai或者bi的字符串
select * from words where word REGEXP 'ai|bi';
还有一个匹配多个字符之一,比如匹配包含a或者b中的任意一个,可以用[ab]来表示。
可以包含数字范围和字母范围,比如从上个例子中匹配多个字符中任意一个来看,如果想要匹配数字0-9的任意一个,也可以表示为[0123456779],在正则里可以表示为[0-9]。
如果想继续匹配字母可以接着拼接[0-9a-zA-Z]表示,这样使用可能还是不够简洁,正则表达式来提供了预定义的字符来简化,比如[0-9]可以用[:digit:]替代表示任意数字,[0-9a-zA-Z]可以用[:alnum:]相当于任意数字和字母,还有很多。
正则表达式很强大,这里仅列了一些日常开发用的比较多的例子,如果遇到复杂的情况也可以针对性的查询,相信只要掌握了使用技巧,上手其他的匹配规则也容易很多。