真正安全的做法是“先查后删”,即先用select明确圈定待删数据,再验证意图、控制范围、留痕可溯,而非直接执行delete。

CI框架里执行删除操作,不能直接写一句$this->db->delete('user', ['id' => 3])就完事。真正安全的做法是“先查后删”,核心在于控制范围、验证意图、留痕可溯。
第一步:用SELECT明确圈定待删数据
不要依赖WHERE条件直接删,先用查询把要删的记录捞出来看一眼。比如:
$this->db->select('*')->from('user')->where('status', 'inactive')->where('created_at get()->result();- 把结果打印或日志记录,确认行数、关键字段(如手机号、身份证号是否已脱敏)、时间范围是否合理
- 若涉及敏感字段,建议额外加校验:比如
COUNT(*)对比脱敏前后身份证格式匹配数,防止脱敏漏项
第二步:建临时表或导出快照再人工复核
尤其在批量删除前,把待删数据单独存一份,避免误操作无法回退:
- 执行
CREATE TABLE user_to_delete AS SELECT * FROM user WHERE ...(需有对应权限) - 或用CI的
$this->dbutil->backup()导出这部分数据为SQL文件,命名含日期和用途,如user_inactive_20260813.sql - 抽查3–5条记录,重点看敏感字段是否已按规则处理(如手机号中间四位是否掩码、身份证是否只保留前六后四)
第三步:优先软删,硬删必加事务与锁
除非业务强要求清空物理数据,否则用UPDATE标记更稳妥:
- 软删示例:
$this->db->update('user', ['status' => 'deleted', 'deleted_at' => date('Y-m-d H:i:s')], ['id' => $id]); - 硬删必须包裹在事务中:
$this->db->trans_start(); $this->db->delete(...); $this->db->trans_complete(); - 若用UPDATE方式脱敏后再删,务必加
SELECT ... FOR UPDATE锁表,防止并发写入导致状态错乱
第四步:执行后验证+留痕
删完不是终点,得确认结果并记录动作:
- 立刻查
SELECT COUNT(*) FROM user WHERE [原删除条件],确认返回0 - 记录日志:包括操作人、时间、影响行数、原始WHERE条件、备份文件路径(如有)
- 若使用
SetWarnings关闭了Access类提示,CI中也要同步关掉自动确认——但仅限脚本内可控场景,开发环境务必保持开启











