不加where的delete会删除表中所有记录,但保留表结构;它是sql标准行为,非数据库bug,mysql需靠sql_safe_updates=1或应用层拦截防范。

不加WHERE的DELETE会删光整张表
这是SQL标准行为,不是数据库bug。执行 DELETE FROM users 这类语句时,数据库不会提示“你确定要删全表吗”,而是直接逐行扫描、逐行标记删除、逐行写日志——在SQL Server里它不重置IDENTITY列,在MySQL里它可回滚但默认不开启事务自动包装,在Oracle里它照样走完整事务链。结果就是:3秒内清空千万级订单表,而备份恢复可能要4小时起步。
MySQL的sql_safe_updates=1能拦住但有硬限制
这个开关只对当前会话生效,且只认主键或唯一索引字段出现在WHERE里。比如这些会被放行:WHERE id = 100、WHERE order_no = 'OD2026'(order_no带UNIQUE约束);但WHERE name = 'Alice'哪怕name有普通INDEX也会报ERROR 1175。它不防拼接SQL漏洞,也不管你是不是在生产环境连的会话——得手动设,还得每个连接都设。
MyBatis-Plus用BlockAttackInnerInterceptor拦截更可靠
这是应用层兜底手段,比数据库配置更贴近业务逻辑。启用后,任何没WHERE的delete或update在执行前就被拦下,抛出BadSqlGrammarException。但它只对MP封装的方法有效(比如userMapper.delete(null)),对原生SqlSession.update("xxx")或@SelectProvider动态SQL无效。部署时容易漏掉非标准DAO调用路径。
真正容易被忽略的是动态SQL拼接场景
Java里"DELETE FROM users " + whereClause这种写法,如果whereClause为空字符串,最终就是裸DELETE;Python里f"DELETE FROM users WHERE {cond}"同样危险。ORM和数据库开关都拦不住这种字符串缝合怪。最稳妥的做法是:所有条件拼接前强制校验whereClause非空,或改用预编译参数绑定(如WHERE id IN (#{ids})),把条件缺失问题提前暴露在编译/测试阶段,而不是等到上线执行才炸。











