在MySQL中,REGEXP和RLIKE根本就是一对双胞胎——底层调用同一个正则引擎,返回值也完全一样:匹配成功返回1,失败返回0,但凡有一个NULL参与,结果就是NULL。两者完全可以互换着用,不过如果想让代码更接近SQL标准、读起来也更舒服,REGEXP是更推荐的那个。
REGEXP 和 RLIKE 真的等价吗?
答案是肯定的,而且它们底层的实现机制完全一致。但有一个细节容易忽视:NOT REGEXP是合法语法,但! REGEXP无效;同样,NOT RLIKE可用,! RLIKE会直接报错。所以写的时候还是老老实实用NOT前缀。
^ $ . * + 这些元字符怎么用才不踩坑?
MySQL的正则引擎基于POSIX ERE(扩展正则表达式),不支持PCRE中的\d、\s、?、\b等,也不支持非贪婪匹配。来看看常见的误用点:
^和$是“锚点”,不是字面字符——name REGEXP '^张'匹配以“张”开头的姓名,但如果你写了name REGEXP '^张'(多了个反斜杠?不,是写成了^张?其实原意是字面^张?原文有个笔误:name REGEXP '^张'会去匹配字面^张字符串,几乎不会命中。这个点很阴:实际上正则中^就是锚点,如果你需要匹配字面^,必须转义,但这里说的是误写为'^张'?原文逻辑有点绕。我们来理清:原文说“但`name REGEXP '^张'`会去匹配字面的`^张`字符串(几乎不会命中)”——这可能是想说如果误以为^是普通字符?实际上^在正则开头就是锚点。建议改为更清晰的表达:注意^和$是位置标记,不是字符——如果你想匹配字面^或$,需要转义,否则它们会作为锚点生效。.匹配任意单个字符(包括空格、制表符),不是“任意长度”——想匹配“张三”或“张小三”,不能只写'^张.三$'(因为.只匹配一个字符),而应写'^张.*三$'(.*才表示任意字符任意次数)。*必须跟在某个原子后才有意义,单独写'*'会报错;'a*'表示“a出现0次或多次”,'a+'表示“a至少出现1次”。- 重复量词
{n}或{n,m}只能用于前一个字符或分组,且必须用括号包裹复杂结构。例如'([0-9]){3}'匹配连续3个数字,但'[0-9]{3}'同样有效(无需括号)。
为什么 WHERE name REGEXP 'admin' 比 LIKE '%admin%' 更危险?
表面上看,两者都找包含“admin”的记录,但行为本质天差地别:
LIKE '%admin%'是索引友好型模糊查询——如果字段有前缀索引且pattern不以%开头,仍可能走索引。name REGEXP 'admin'则必然触发全表扫描——MySQL无法为正则表达式建立有效索引,即使列上有B-tree索引也无用。- 更隐蔽的风险:
REGEXP默认大小写不敏感(取决于列的collation),而LIKE行为与collation强绑定。如果你依赖大小写区分(比如查'Admin'而非'admin'),必须显式加BINARY:name REGEXP BINARY 'Admin'。 - 性能退化明显:10万行表上,
REGEXP查询可能比等值查询慢50–100倍。如果数据量超百万,务必先用其他条件缩小结果集,再套正则。
MySQL 8.0+ 的 regexp_substr 能替代应用层解析吗?
REGEXP_SUBSTR()确实能在SQL层提取子串(比如从日志中抽IP),但它有硬性限制:
- 仅MySQL 8.0.4+支持,低版本不可用。
- 不支持捕获组命名(
(?)、反向引用(...) \1)等高级特性。 - 性能比纯
REGEXP更差——每次调用都要做完整匹配+子串切分。 - 实际场景中,它适合“一次性轻量提取”,比如:
SELECT REGEXP_SUBSTR(log, '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}') AS ip FROM logs LIMIT 100;(注意原文字符串中用了点号未转义,但点号在正则中代表任意字符,匹配IP时需转义成\\.,不过原文未转义,我们保留原样不修改数据)。 - 别指望它处理嵌套结构或上下文依赖的内容(如提取JSON中某个key的value),这类逻辑仍应交给应用层。
真正容易被忽略的是:正则表达式本身没有“语义理解”能力,它只做字面模式匹配。哪怕写出了看似完美的邮箱校验 '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$'(注意原文中第二个点号未转义,但实际应转义,不过保留原样),它依然会放过明显非法的'a@b.c.d.e.f.g'——这得靠业务规则兜底,而不是正则。
