触发器校验需用RAISE/SIGNAL中断插入,配合TRIM和简单正则或长度检查;须早于约束执行,注意NULL与空字符串区分,且必须测试单行、多行VALUES及INSERT...SELECT场景。

INSERT 触发器里怎么写格式校验
直接在触发器里用 RAISE 或 SIGNAL 中断插入,配合正则或字符串函数做校验。别指望靠应用层兜底——触发器是最后一道防线,得自己拦住非法数据。
常见错误是把校验逻辑写成纯 SELECT 判断,没抛异常,结果插入照常发生;或者用 IF NOT ... THEN RETURN(MySQL 5.7+ 不支持触发器中 return),白写。
- PostgreSQL 用
RAISE EXCEPTION 'xxx' - MySQL 8.0+ 用
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'xxx' - SQLite 用
RAISE(ABORT, 'xxx')(注意必须是 ABORT,FAIL/IGNORE 不会阻止 INSERT)
邮箱、手机号这类字段的正则校验怎么写才靠谱
别抄网上“万能邮箱正则”——太长、兼容差、还可能被绕过。触发器校验要的是快 + 足够防傻,不是 RFC 合规。
比如邮箱,NEW.email NOT REGEXP '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$' 在 MySQL 8.0+ 可用;PostgreSQL 用 NEW.email !~ '^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$'(注意大小写,加 ILIKE 或 LOWER() 处理更稳)。
- 手机号建议限定国家码前缀 + 长度,比如
LENGTH(REPLACE(REPLACE(NEW.phone, '-', ''), '+', '')) = 11比正则更准更轻 - 空格、全角字符容易漏,校验前先
TRIM()和REPLACE(NEW.field, ' ', '')(全角空格) - 正则在触发器里执行频繁时有性能开销,简单长度/字符集检查优先于复杂正则
触发器校验和 DEFAULT、NOT NULL 冲突吗
会。如果字段定义了 NOT NULL,但触发器里又用 NEW.field := NULL 赋值,MySQL 会报错 Column 'xxx' cannot be null,且错误发生在触发器执行之后——你拦不住。
所以校验逻辑必须早于任何可能引发约束冲突的操作。尤其注意:MySQL 触发器中修改 NEW 字段,不会跳过列级约束检查。
- 想自动补默认值?先确保字段允许 NULL,再在触发器里赋值,最后靠应用或后续逻辑保证非空
- 想拦截空字符串当非法输入?不能只判
NEW.field = '',还得判NEW.field IS NULL,两者语义不同 - PostgreSQL 对
NEW字段赋值更自由,但NOT NULL约束仍会在触发器后强制校验
测试触发器校验逻辑时最容易忽略什么
批量插入(INSERT ... SELECT 或 LOAD DATA)下,触发器对每一行单独执行,但错误只报第一行——后面的数据可能静默失败或部分插入,查日志都难定位。
还有:事务回滚时,触发器里的副作用(比如写日志表)未必回滚,除非你显式用 AUTONOMOUS TRANSACTION(仅 Oracle 支持),其他数据库基本做不到。
- 测试一定要覆盖单行
INSERT、多行VALUES、INSERT ... SELECT三种场景 - 用
SHOW WARNINGS(MySQL)或DO $$ ... $$块捕获异常(PG)来验证是否真拦住了 - 别在触发器里调用存储过程做校验——堆栈深、调试难、权限易出问题
触发器校验看着简单,实际卡点都在边界:NULL 和空字符串的区分、批量操作的静默失败、约束检查与触发时机的竞态。写完不测多行插入,等于没写。

















