BEFORE DELETE触发器不能自动阻止删除,必须用SIGNAL显式抛异常中断;校验仅能依赖OLD字段,禁止子查询;TRUNCATE/DROP不触发触发器,须配合权限管控与日志告警。

BEFORE DELETE 触发器必须用 SIGNAL 主动中断
触发器不会自动阻止删除——它只提供一个检查时机。不显式抛出异常,DELETE 语句照常执行,哪怕你写了 SELECT 'blocked' 或 INSERT INTO audit_log 都无效。唯一可靠方式是:SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '禁止删除管理员账户';
校验逻辑只能读 OLD 字段,不能查其他表
想拦住特定管理员(比如 username = 'admin' 或 role = 'super'),必须直接判断 OLD.username 或 OLD.role。以下写法全部错误:
-
SELECT COUNT(*) FROM users WHERE id = OLD.id AND is_admin = 1—— 子查询禁止,易死锁 -
IF (SELECT is_admin FROM users WHERE id = OLD.id) THEN ...—— 同上,且违反单行校验原则 -
IF OLD.id IN (1, 2, 100) THEN ...—— 硬编码 ID 不可维护,且无法覆盖用户名变更场景
推荐做法:在用户表加冗余字段 is_protected TINYINT DEFAULT 0,由运维手动置 1;触发器只判 IF OLD.is_protected = 1 THEN SIGNAL ...
多行删除时,OLD 每次只代表一行
执行 DELETE FROM users WHERE role IN ('super', 'admin'); 时,触发器对每行分别触发一次,OLD 始终是当前被删的那行数据。你不需要、也不能写“查所有匹配行再统一拦截”——MySQL 不支持 SELECT * FROM OLD,会报错 Unknown table 'OLD'。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
这意味着:
- 只要某行满足保护条件(如
OLD.is_protected = 1),该行删除就会被中断,整条DELETE语句回滚 - 但若误删语句漏写
WHERE(如DELETE FROM users;),触发器仍逐行检查,只要有一行受保护,就整个失败 - 无法区分“删 admin 用户”和“删普通用户”,只能靠字段值判断,所以字段设计要明确、稳定
TRUNCATE 和 DROP 完全不触发任何触发器
这是硬限制:TRUNCATE TABLE users、DROP TABLE users、DROP DATABASE mydb 都不会调用 BEFORE DELETE 触发器。它们属于 DDL,而触发器只响应 DML。
真正有效的兜底手段只有:
-
REVOKE DROP, TRUNCATE ON mydb.users FROM 'dev'@'%';—— 权限卡死 - 为高危表禁用
DELETE权限,改用软删除(如deleted_at DATETIME)+ 应用层控制 - 开启通用日志 + 关键词告警(匹配
TRUNCATE TABLE、DROP TABLE)
最容易被忽略的是:触发器只防“删某行”,不防“删整表”。防线必须是权限 + 日志 + 备份三层叠加,不能只押注触发器逻辑本身。

















