靠“先查后删 + 手动事务 + sql_safe_updates”三件套最有效:执行delete前必用select验证where条件,开启手动事务并设sql_safe_updates=1,辅以软删除、分批删和二进制日志保障。

直接结论:靠“先查后删 + 手动事务 + SQL_SAFE_UPDATES”三件套,比任何口头提醒都管用。
执行DELETE前必须用SELECT验证WHERE条件
写了WHERE不等于写对了——隐式类型转换、OR/AND优先级混乱、模糊匹配失控(如LIKE '%admin%')都会让实际影响行数远超预期。
- 把
DELETE FROM orders WHERE status = 'canceled' AND created_at 改成<code>SELECT COUNT(*) FROM orders WHERE status = 'canceled' AND created_at 先跑一遍 - 再补一句
SELECT * FROM orders WHERE status = 'canceled' AND created_at 看具体数据是否符合业务语义 - 时间字段别裸写
'2025-01-01',统一用STR_TO_DATE('2025-01-01', '%Y-%m-%d')或标准格式'2025-01-01 00:00:00',避免索引失效
Navicat连接生产库后第一件事:关自动提交
Navicat默认AUTOCOMMIT = 1,意味着DELETE敲下去就永久生效,没机会反悔。图形界面里的“自动提交开关”不可靠,且版本间位置不一致,必须用SQL命令控制。
- 每次打开新标签页连生产库,第一行必须是:
SET AUTOCOMMIT = 0; - 紧接着加一句:
SET SQL_SAFE_UPDATES = 1;——它会拦截无WHERE或WHERE不走索引的DELETE/UPDATE -
SET AUTOCOMMIT = 0只对当前会话有效,关掉标签页就失效,不能省略 - 执行完
DELETE后,立刻看Navicat底部状态栏的Affected rows,或手动查SELECT ROW_COUNT();
MySQL触发器拦截不是万能的,但能堵住最野的操作
在关键表上建BEFORE DELETE触发器,本质是“只要有人发DELETE,就强制中断并报错”,但要注意它不拦TRUNCATE、不拦DROP、也不拦跨库误操作。
- MySQL 8.0+ 正确写法:
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'DELETE disabled on production table orders'; - 千万别只写
SELECT 'blocked';或PRINT——这些只是输出,不会中断事务 - 触发器必须用
DEFINER高权限账号创建,否则可能因权限不足静默失败 - 如果表有级联外键(
ON DELETE CASCADE),SQL Server 和 MySQL 都不允许建INSTEAD OF/BEFORE触发器,建会直接报错
软删除和分批删除是降低风险的实际手段
硬删不是唯一选项,尤其对用户行为、订单、日志这类关键数据,软删+异步归档更可控;而大表批量删,LIMIT不是锦上添花,是防止锁表拖垮服务的刚需。
- 加
is_deleted TINYINT DEFAULT 0字段,用UPDATE SET is_deleted = 1替代DELETE,查询时加WHERE is_deleted = 0 - 真要物理删,别写
DELETE FROM logs WHERE created_at ,改用<code>DELETE FROM logs WHERE created_at - 分批删时,必须带
ORDER BY id,否则可能跳过某些行(InnoDB二级索引扫描非确定性) - 所有删操作前,确保
log_bin = ON,误删后可用mysqlbinlog解析日志回滚
真正危险的不是不会写SQL,而是“以为自己写对了”。SELECT验证、SET AUTOCOMMIT = 0、SQL_SAFE_UPDATES这三步,必须变成肌肉记忆——少走一步,就少一道屏障。











