做数据库开发和运维这些年,我越来越觉得正则表达式是个典型的“平时用不上、一用就真香”的技能。SQL里的REGEXP正则表达式,就是把这套文本模式匹配能力搬进数据库查询中,让你不用把几百万行数据导到应用层,在SQL层面就能完成字符串筛选、格式校验、内容提取和批量清洗。不少朋友在热搜里搜“正则表达式语法”“REGEXP怎么用”“为什么用REGEXP查询特别慢”,说明大家对这个功能有兴趣,但又被各种版本差异和坑折磨过。这篇就把REGEXP的语法、函数、跨数据库差异、性能问题和排查经验一次讲透,按我实际踩过的坑来讲,不绕弯子。
先说说这篇适合谁。一是业务开发,经常要对用户输入、手机号、邮箱、地址做模糊匹配;二是数据分析师和数据治理的同学,面对一堆脏数据要清洗归类;三是DBA和运维,排查慢SQL时发现大表加上REGEXP后CPU直接飙红。我给的方法都能直接用,但更重要的是把每条规则背后的逻辑讲明白,你才知道什么时候该用,什么时候千万别用。
1. 从LIKE到REGEXP:模糊查询能走多远
1.1 LIKE的边界在哪
先说一个最常见的场景:用户表里有几十万条手机号,有的带空格,有的带横线,有的是纯数字。领导要求把格式不规范的记录挑出来。用LIKE怎么写?LIKE '%138%'会把规范和不规范的全捞出来,因为只要包含“138”这三个连续字符就命中。想表达“必须是11位数字”这种结构规则,LIKE直接没辙。
LIKE本质上只有三种语义:以什么开头(%放后面)、以什么结尾(%放前面)、包含什么(两边都有%)。在SQL Server的LIKE里还能用[0-9]这种字符集做单字符匹配,但MySQL的LIKE连这个都不支持。一旦需求变成“第4位到第7位必须是某个范围”“必须由纯数字组成”“允许两种格式同时存在”,LIKE就会逼着你写出一长串OR条件,又丑又容易漏。
1.2 REGEXP解决的是“结构匹配”问题
REGEXP进入SQL生态后,情况完全不同。它允许你用一套描述性的字符串规则,去匹配另一个字符串是否符合这个规则。打个比方:LIKE像你告诉快递员“收货人名字里带‘张’”,而正则像是给快递员一张“地址格式卡”——必须包含省市区、街道名不能超过多少位、门牌号必须是数字。快递员按规则校验,而不是碰运气找关键字。
在MySQL里,最简单的用法是这样:
sql复制SELECT * FROM user WHERE phone REGEXP '^1[3-9][0-9]{9}$';
这句话的规则是:以数字1开头,第二位是3到9之间的任意数字,后面跟9位0到9的数字,总共11位。这个规则用LIKE是写不出来的。你可以直接用这行SQL扫描整张表,把不合规的手机号一条不漏地揪出来。这就是REGEXP的第一个价值:把“模式判断”下沉到数据库,减少数据传输,也减少应用层写循环匹配的代码量。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础语法:SQL里写正则的正确姿势
2.1 常用元字符速查
正则的元字符是通用的,但在SQL里写的时候要确认当前数据库支持到什么程度。我整理了一份常用元字符表,覆盖绝大多数业务场景:
| 元字符 | 含义 | 示例 |
|---|---|---|
. |
匹配任意单个字符(换行除外) | a.c 匹配 abc |
* |
前一个字符出现0次或多次 | ab*c 匹配 ac、abc、abbc |
+ |
前一个字符出现1次或多次 | ab+c 匹配 abc,不匹配 ac |
? |
前一个字符出现0次或1次 | ab?c 匹配 ac、abc |
^ |
匹配字符串开头 | ^1 表示以1开头 |
$ |
匹配字符串结尾 | 9$ 表示以9结尾 |
[] |
字符集合 | [0-9] 表示任意数字,[a-zA-Z] 表示任意字母 |
[^] |
取反集合 | [^0-9] 表示非数字 |
() |
分组 | (ab)+ 匹配 ab、abab |
| |
或 | a|b 匹配 a 或 b |
{n} |
重复n次 | [0-9]{3} 匹配3位数字 |
{n,m} |
重复n到m次 | [0-9]{2,4} 匹配2到4位数字 |
\d |
数字,等价于[0-9](部分数据库支持) |
MySQL 8.0+支持 |
\w |
字母、数字、下划线 | 部分数据库支持 |
\s |
空白字符 | 部分数据库支持 |
需要特别留意POSIX字符类,在旧版MySQL中非常常用:[[:digit:]]表示数字,[[:alpha:]]表示字母,[[:alnum:]]表示字母和数字,[[:space:]]表示空白。如果你维护的MySQL版本还没升到8.0,用它比\d更稳。
2.2 谓词与函数:REGEXP、REGEXP_LIKE及其配套
MySQL的用法比较典型。5.x时代主要用expr REGEXP 'pattern'这种谓词写法,返回0或1。8.0之后提供了更完整的函数族:
- `REGEXP_LIKE(ex
