数据验证必须用BEFORE INSERT(及BEFORE UPDATE),因其可在写入前拦截非法数据、修正字段值或主动报错;AFTER仅适用于日志记录或联动更新,无法阻止脏数据入库。
MySQL触发器里用AFTER INSERT还是BEFORE INSERT?
数据验证必须用before insert(以及before update),否则验证失败时数据已经写进表里,再抛错也拦不住脏数据。用after只能做日志或联动更新,不能阻止非法值入库。
-
BEFORE中可用SET NEW.column = ...修正值,比如把空字符串转为NULL - 验证不通过要主动报错:
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '年龄必须在0-150之间'; - Navicat里建触发器时,类型下拉框选“Before”而不是默认的“AFTER”——这个选项藏得深,容易点错
Navicat里创建触发器时提示ERROR 1419: You do not have SUPER privilege
这是MySQL 5.7+默认开启sql_mode='STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'后,对触发器权限的收紧。Navicat执行CREATE TRIGGER时会自动加DEFINER,而普通账号没SUPER权限就卡住。
- 临时解法:连上MySQL后先执行
SET SQL_LOG_BIN = 0;(仅限测试环境) - 长期方案:让DBA给你账号加
TRIGGER权限:GRANT TRIGGER ON your_db.* TO 'user'@'%'; - Navicat建触发器界面右下角有“Definer”字段,清空它或改成当前登录用户,能绕过部分校验
验证逻辑写在触发器里,为什么INSERT INTO t VALUES ('', 200)没报错?
常见原因是触发器没生效:要么是表名写错(大小写敏感,尤其Linux服务器),要么是触发器创建在错误的数据库下,或者Navicat连的是test库但表在prod库。
- 检查触发器是否真存在:
SELECT * FROM information_schema.TRIGGERS WHERE EVENT_OBJECT_TABLE = 'your_table'; - 触发器里的判断别用
IF '' = NEW.name,要用IF NEW.name IS NULL OR TRIM(NEW.name) = '',否则空格或NULL会漏判 - MySQL对中文报错信息支持弱,
MESSAGE_TEXT里别写复杂Unicode,用英文或拼音缩写更稳,比如'age_out_of_range'
用Navicat导出触发器SQL时,DELIMITER被吃掉了怎么办?
Navicat“导出向导”默认只导结构和数据,触发器SQL里的DELIMITER $$和结尾$$会被过滤掉,直接执行会报语法错。
- 正确做法:右键触发器 → “备份对象” → 勾选“包含CREATE语句”,生成的SQL才带完整分隔符
- 如果已导出且缺
DELIMITER,手动在CREATE TRIGGER前加DELIMITER $$,在END后加$$,再补一句DELIMITER ; - 注意Navicat不同版本对
DEFINER处理不一致,导出后再导入时可能因用户不存在而失败,建议提前确认目标库账号一致
触发器不是万能验证层,它不拦截LOAD DATA INFILE、INSERT ... SELECT跨表操作,也不管应用层直连绕过ORM的行为。真要防死,得配合应用层校验+数据库约束(比如CHECK约束,MySQL 8.0.16+才支持)。










