必须用 SIGNAL SQLSTATE '45000' 主动中断,仅写日志或执行 SELECT 1/0 等伪报错语句完全无效;校验须基于 OLD 字段直判,禁用子查询与函数;TRUNCATE 和 DROP 不触发触发器,需权限管控兜底。

必须用 SIGNAL 主动中断,仅写日志或执行报错语句(如 SELECT 1/0)完全无效。
BEFORE DELETE 触发器里怎么真正拦住删除?
MySQL 的 BEFORE DELETE 触发器不会自动阻止操作——它只提供一个校验时机。你得显式抛出异常才能终止语句并回滚当前行。
-
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '禁止删除活跃用户';是唯一可靠方式 -
SELECT 'error' FROM DUAL WHERE 1=0或RAISERROR(SQL Server 写法)根本不起作用,DELETE 会照常提交 - 触发器中不能用
ROLLBACK—— MySQL 不允许在触发器内手动控制事务边界 - 错误信息会原样返回客户端,比如
ERROR 1644 (45000): 禁止删除活跃用户
校验逻辑该读什么数据?
只能依赖 OLD,别查别的表,也别幻想有 deleted 表。
-
OLD.status、OLD.created_at这类字段才是你要判断的依据 - 想检查“是否还有未完成订单”,别在触发器里写
SELECT COUNT(*) FROM orders WHERE user_id = OLD.id—— 容易拖慢、死锁,且违反单行校验原则 - 真要关联状态,提前把关键字段冗余进本表,比如加个
is_deletable TINYINT DEFAULT 1,触发器只读这一列 - 多行删除时,
OLD每次只代表一行,SELECT COUNT(*) FROM OLD语法非法,MySQL 直接报错Unknown table 'OLD'
哪些删除操作根本拦不住?
TRUNCATE TABLE 和 DROP TABLE 完全不触发任何触发器,这是硬限制。
-
BEFORE DELETE只响应 DML,对 DDL(数据定义语言)无感 - 哪怕你在表上建了十个触发器,
TRUNCATE TABLE users依然畅通无阻 - 唯一有效手段是权限控制:
REVOKE DROP, TRUNCATE ON mydb.users FROM 'dev'@'%'; - 对高危表,建议禁用
DELETE权限,改用软删除字段(如deleted_at DATETIME),由应用层统一控制
最易被忽略的是:触发器只管“删某行”,不管“删多少行”;它也不管你是手敲 SQL 还是 ORM 自动生成——只要进了 DELETE 流程,就按规则走。但一旦绕过 DML(比如用 mysqldump --where 导出再清空),或直接删库,所有触发器都成摆设。防线得靠权限 + 审计日志 + 备份三层叠加,不能只押注触发器。


















