不能。MySQL触发器无法在BEFORE DELETE中将DELETE转为UPDATE,因SQL解析器已确定操作类型;执行UPDATE会报错或导致物理删除覆盖软删,逻辑删除失效;正确做法是禁用DELETE权限,改用BEFORE UPDATE实现软删,并确保所有查询过滤is_deleted=0。

不能。 MySQL 触发器无法在 BEFORE DELETE 中把一条 DELETE 语句“变成” UPDATE ——SQL 解析器早已确定这是 DELETE 操作,触发器只能响应它,不能篡改语句类型。
BEFORE DELETE 触发器里执行 UPDATE 不等于拦截删除
你可以在 BEFORE DELETE 里写 UPDATE users SET is_deleted = 1 WHERE id = OLD.id,这条 UPDATE 会成功执行,但原 DELETE 仍会继续运行(除非显式 SIGNAL 中断)。结果是:数据先被软删,再被物理删掉,逻辑删除彻底失效。
- MySQL 不允许在
BEFORE DELETE触发器中修改触发表本身(即对OLD所在表做UPDATE),5.7 会直接报错ERROR 1442 (HY000): Can't update table 'users' in stored function/trigger because it is already used by statement which invoked this stored function/trigger. - 即使绕过限制(比如用存储过程+事件或应用层重试),行为也违背直觉:用户执行
DELETE却发现记录还在,且返回0 rows affected,极易引发误判和调试困难
真正该用的时机是 BEFORE UPDATE
逻辑删除的本质是“用户想删,你转成标为已删”,这个意图最自然的载体是 UPDATE 操作本身,比如 UPDATE users SET is_deleted = 1 WHERE id = 123。这时 BEFORE UPDATE 是唯一能安全干预的入口:
- 检查
OLD.is_deleted = 0 AND NEW.is_deleted = 1,就自动补全NEW.deleted_at = NOW()、NEW.deleted_by = @current_user - 若发现
OLD.is_deleted = 1,可直接SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Read-only for deleted records';阻止后续修改 - 所有字段操作都在
NEW上完成,不触碰原表数据,无递归风险
DELETE 语句必须从权限层面禁用
只靠触发器控制 UPDATE 远不够。如果应用或运维人员直接发 DELETE FROM users WHERE id = 123,BEFORE UPDATE 根本不触发,逻辑删除形同虚设。
- 立刻执行
REVOKE DELETE ON mydb.users FROM 'app_user'@'%',撤掉应用账号的DELETE权限 - DBA 如需清理,应走专用通道:比如创建带校验的存储过程
sp_soft_delete_user(IN p_id INT),或使用视图 +INSERT/UPDATE代理 - 切勿依赖
BEFORE DELETE做日志记录后放行——那只是“留痕删除”,不是逻辑删除
最易被忽略的一点:逻辑删除不是技术功能,而是数据契约。一旦上线,所有 SELECT 必须默认加 WHERE is_deleted = 0,连 JOIN 和子查询都不能漏;否则哪怕触发器写得再严密,数据一致性也会在查询层崩塌。


















