误删主因是where条件错误或缺失;例如将created_at > '2023-01-01'错写为=,导致删新数据;执行前须用select count(*)验证范围。

DELETE 语句里 WHERE 时间条件写错会导致全表误删
时间范围删除最危险的坑是漏写 WHERE 或写错时间比较逻辑,比如把 created_at 误写成 <code>created_at > '2023-01-01',结果删掉的是新数据。务必在执行前用 SELECT COUNT(*) 验证范围:
SELECT COUNT(*) FROM logs WHERE created_at 确认数量合理再删。
MySQL 和 PostgreSQL 对时间字段类型处理差异明显
MySQL 的 DATETIME 和 TIMESTAMP 在时区行为上不同:前者存字面值,后者存 UTC;PostgreSQL 默认按会话时区解析字符串。例如 '2023-01-01' 在 PostgreSQL 中可能被解释为本地时区的午夜,而在 MySQL 中默认当作无时区时间。建议统一用带时区的格式:'2023-01-01T00:00:00+00:00',或显式转换:created_at AT TIME ZONE 'UTC'(PostgreSQL)、CONVERT_TZ(created_at, '+00:00', @@session.time_zone)(MySQL)。
大表删数据必须分批,否则锁表或 OOM
单次删几百万行容易触发长事务、锁表、日志暴涨甚至内存溢出。不要直接 DELETE FROM events WHERE created_at 。改用循环分页删:<pre class="brush:php;toolbar:false;">DELETE FROM events WHERE id IN (SELECT id FROM events WHERE created_at 每次删 1 万条,配合 <code>SLEEP(0.1)</code>(MySQL)或应用层延迟控制节奏。注意子查询不能直接引用同表,需加中间别名绕过限制:<code>(SELECT id FROM (SELECT id FROM events WHERE created_at 。</code></pre>
WHERE 条件中用函数会导致索引失效
像 DATE(created_at) 或 <code>YEAR(created_at) = 2022 这类写法会让数据库无法使用 created_at 上的索引,全表扫描。正确做法是把函数移到右边:created_at ,或者用范围表达:<code>created_at >= '2022-01-01' AND created_at 。如果只有日期字段没时间部分,确保字段类型是 <code>DATE,且查询也用 date_column 而非 <code>STR_TO_DATE(...)。
真正麻烦的不是语法,而是时间字段是否含时区、索引是否覆盖查询条件、以及删的过程中有没有其他写操作在争抢资源——这些细节不提前看执行计划和慢日志,删着删着就卡住或超时了。











