不能,MySQL 8 的 CHECK 约束不支持原生正则;需封装 DETERMINISTIC 函数调用 REGEXP,并在函数内处理 NULL/空值,建表时配合 NOT NULL,且前后端仍须双重校验。
MySQL 8 的 CHECK 约束能直接写正则吗?
不能。mysql 8 的 check 约束不支持原生正则语法(比如 regexp 或 rlike),直接写会报错:error 3819 (hy000): check constraint 'xxx' is violated. 或更早版本直接拒绝建表。这是因为 sql 标准中 check 要求表达式必须是确定性(deterministic)且可静态评估的,而 mysql 目前把 regexp 视为非确定性函数(尤其在某些字符集或排序规则下),禁止在 check 中调用。
实操建议:
- 改用
LIKE模糊匹配做简单格式校验(如手机号前缀:phone LIKE '1[3-9]%' AND LENGTH(phone) = 11) - 若必须用正则,得绕道:定义一个
DETERMINISTIC的存储函数封装REGEXP,再在CHECK中调用它 - 注意该函数必须显式声明
DETERMINISTIC,否则建表仍失败
怎么写一个能在 CHECK 中用的正则校验函数?
关键不是“能不能写正则”,而是“MySQL 让不让你在 CHECK 里调它”。唯一可行路径是自己包一层函数,并向 MySQL 承诺它是确定性的。
实操建议:
- 函数名要清晰,比如
is_email_format,避免用validate_这类易被误判为非确定性的前缀 - 必须包含
DETERMINISTIC声明,且函数体只用确定性操作(REGEXP在 MySQL 8.0.22+ 已被允许用于 DETERMINISTIC 函数,但需确认你的小版本) - 别在函数里查表、调用
NOW()、RAND()等非确定性操作,否则建表时会提示Function 'xxx' is not deterministic
示例(邮箱校验):
CREATE FUNCTION is_email_format(s TEXT)
RETURNS BOOLEAN
DETERMINISTIC
BEGIN
RETURN s REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
END;
然后建表时引用:
CREATE TABLE users ( email VARCHAR(255), CONSTRAINT chk_email_format CHECK (is_email_format(email)) );
为什么加了 CHECK 正则函数,INSERT 还是成功了空字符串或 NULL?
因为 CHECK 约束对 NULL 默认不触发——这是 SQL 标准行为。如果字段允许 NULL,且你传入 NULL 或空字符串(''),只要正则函数返回 NULL(而非 FALSE),约束就视为“未违反”。
实操建议:
- 在函数内部显式处理
NULL和空值,统一返回FALSE,例如:IF s IS NULL OR s = '' THEN RETURN FALSE; END IF; - 建表时配合
NOT NULL,堵住NULL入口:email VARCHAR(255) NOT NULL - 测试时别只测正确格式,一定要测
INSERT INTO users (email) VALUES ('');和INSERT INTO users (email) VALUES (NULL);
前端输错格式,后端要不要再校验?
要。MySQL 的 CHECK 是最后一道防线,不是唯一防线。它只在数据写入时生效,无法拦截应用层逻辑错误、绕过 ORM 的直连、或批量导入时的脏数据。
实操建议:
- 界面输入框用
pattern属性 + 前端 JS 正则实时提示(但不可信,仅体验优化) - 后端接收参数后,用同套正则再校验一次,失败直接返回 400,不交到底层 DB
- 日志里记录被
CHECK拦截的异常(通过SHOW ENGINE INNODB STATUS或错误日志搜Check constraint),反推是否有漏校验路径
真正容易被忽略的是:CHECK 错误不会告诉你具体哪条规则失败,只报约束名。所以函数命名、约束名、字段名最好保持语义一致,比如 chk_user_phone_format 对应 is_phone_format() 函数,排查时才不至于对着 CONSTRAINT chk_abc123 发呆。


















