必须用BEFORE INSERT触发器配合SIGNAL SQLSTATE '45000',因为AFTER触发器中数据已写入,SIGNAL仅报错无法回滚,导致非法数据落库;校验需用CAST(NEW.amount AS CHAR)结合LOCATE和LENGTH检查原始小数位数,并TRIM去空格。

必须用 BEFORE INSERT 触发器配合 SIGNAL SQLSTATE '45000',AFTER 类型完全无效——它拦不住已发生的插入。
为什么 SIGNAL 在 AFTER 触发器里没用
AFTER INSERT 触发器运行时,数据已经写入表中。此时调用 SIGNAL 只会让语句报错,但无法回滚那行数据;除非整个事务被上层显式回滚,否则非法数据早已落库。
- MySQL 不允许在
AFTER触发器中修改触发表,更不能撤回刚插进去的那条记录 - 错误信息会显示,但业务上等于“先污染再报警”,审计和修复成本陡增
- 如果你看到
SIGNAL没拦住插入,第一反应该查触发器类型是不是写成了AFTER
怎么写一个真正生效的金额精度校验触发器
对 DECIMAL(10,2) 字段,重点不是判断“是不是数字”,而是检查原始输入的小数位数是否超标。数值比较会绕过精度问题,因为 MySQL 已经悄悄四舍五入了。
- 用
CAST(NEW.amount AS CHAR)获取字符串形态,再用LOCATE('.', value)找小数点位置 - 小数位数 =
LENGTH(value) - LOCATE('.', value)(注意处理无小数点情况) - 务必加
TRIM()去首尾空格,否则"123.45 "会被 CAST 成"123.45"躲过检查 - 字段名要动态进错误消息:
CONCAT('Field ''amount'' exceeds 2 decimal places')
批量插入时性能掉得厉害,怎么办
每一条 INSERT VALUES (),(),() 或 LOAD DATA INFILE 的每一行,都会单独执行一次触发器逻辑。10 万行 = 10 万次字符串转换 + 定位 + 长度计算。
- 单行测试没问题,上线后批量导入变慢十倍,大概率是这个原因
- 别指望“优化触发器内部逻辑”能根本解决——瓶颈在执行频次,不在单次耗时
- 如果业务允许,把精度校验前移到应用层或入库前清洗环节,数据库只做兜底
- 真要在 DB 层硬扛,至少避免在触发器里调用自定义函数或查其他表,那会雪上加霜
最常被忽略的一点:SIGNAL 消息里拼接 NEW.xxx 字段时,必须确认它非 NULL;一旦 NEW.id 是 NULL,整个 SIGNAL 语句就静默失效——既不报错,也不拦截。


















