regexp和rlike是mysql中等价的正则匹配操作符,非函数;返回1或0,不支持d等简写,需用[0-9];必须注意^$锚点、转义、版本兼容性及性能限制。

REGEXP 和 RLIKE 是 MySQL 中做高级文本匹配最直接、最常用的手段,但它们不是“函数”,而是操作符——这点常被误当成函数调用而写错语法。
REGEXP 与 RLIKE 的实际行为差异
- 两者完全等价,
RLIKE就是REGEXP的同义词,不推荐混用,统一选一个(建议用REGEXP,更符合 SQL 风格直觉) - 返回值是整数:匹配成功返回
1,失败返回0,不能直接用于ORDER BY或算术计算而不显式转换 - 不支持
d、w、s这类简写(MySQL 8.0+ 仍不支持),必须写成[0-9]、[a-zA-Z0-9_]、[ ]
常见错误现象:
-
WHERE email REGEXP 'd{3}'→ 报错或不匹配,应写为'[0-9]{3}' -
SELECT * FROM t WHERE name REGEXP 'John' ORDER BY REGEXP→ 语法错误,REGEXP不是列名
如何正确匹配邮箱、手机号这类复杂格式
关键不是“写全”,而是分段验证 + 边界控制。比如邮箱:
WHERE email REGEXP '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
注意点:
-
^和$必须加上,否则'@gmail.com'会匹配到'xxx@gmail.com.xxx'这种非法串 -
.在域名后必须转义为.,否则'@gmail.com'中的.会匹配任意字符 -
[a-zA-Z]{2,}比.[a-z]{2,}更安全,避免顶级域被.泄露匹配范围
手机号示例(国内):
WHERE phone REGEXP '^1[3-9][0-9]{9}$'
-
[3-9]排除早期已停用号段(如 12x、14x 部分号段),比^1[0-9]{10}$更贴近真实校验 - 不要漏掉
$,否则'138123456789'(12位)也会被误判为匹配
REGEXP_REPLACE 等扩展函数的使用陷阱
MySQL 8.0+ 才支持 REGEXP_REPLACE、REGEXP_SUBSTR 等函数,低版本会直接报错 FUNCTION xxx does not exist
使用前务必确认:
-
SELECT VERSION();是否 ≥ 8.0 - 替换时反斜杠要双写:
'--',单写'--'会被 MySQL 当作字符串转义处理,导致捕获组失效 -
REGEXP_INSTR默认从位置 1 开始搜,但若字段值以空格开头,start => 2可能跳过首字符匹配
典型误用:
-- 错误:MySQL 5.7 下运行直接报错 SELECT REGEXP_REPLACE(name, '([A-Z])[a-z]+', '\1'); -- 正确(8.0+)且注意双反斜杠 SELECT REGEXP_REPLACE(name, '([A-Z])[a-z]+', '\1');
大小写、性能与可维护性的真实取舍
- 默认不区分大小写,想严格匹配得加
BINARY:WHERE name REGEXP BINARY '^Admin$'
但BINARY会强制走全表扫描,索引失效,大数据量慎用 - 更轻量的替代方案是统一转换:
WHERE LOWER(name) REGEXP '^admin$',虽多一次函数计算,但兼容性好、可走前缀索引(如果字段有LOWER(name)的函数索引) - 正则越长,执行越慢;
[a-zA-Z0-9._%+-]+比.+安全得多,也快得多——后者容易触发回溯爆炸(ReDoS)
真正容易被忽略的一点:
正则表达式在 WHERE 中无法利用普通 B-tree 索引加速,除非你提前建好生成列 + 函数索引(MySQL 8.0.13+),例如:
ALTER TABLE users ADD COLUMN email_domain VARCHAR(64) GENERATED ALWAYS AS (SUBSTRING_INDEX(email, '@', -1)) STORED; CREATE INDEX idx_email_domain ON users(email_domain);
然后用 WHERE email_domain = 'gmail.com' 替代正则匹配,这才是高并发场景下的合理解法。











