首页/数据/SQL 速查/删除关联数据:多表 DELETE 与 USING
变更与事务52 条

删除关联数据:多表 DELETE 与 USING

按另一张表的条件删除:MySQL 用多表 DELETE ... FROM t1 JOIN t2,PostgreSQL 用 DELETE ... USING。

SQL 速查数据更新于 2026-10-13

按另一张表的条件删除

删除条件来自另一张表的场景很常见:把已退款订单删掉、把无引用的配置清理掉。两种数据库的语法结构不同,最要命的差别在 MySQL——多表删除语句里,真正被删的是别名清单里列出的那张表,写错别名会得到「语句成功执行但删了零行」或者删错表的结果,而且都不会报错。执行前用同样的条件先跑一次查询统计行数,是这里唯一可靠的防线。

基本信息

适用平台MySQL 8.0 / PostgreSQL 12 及以上
MySQL 写法删除关键字后列出要删的别名,再写连接条件
PostgreSQL 写法目标表后跟辅助表清单,再写条件
别名规则MySQL 决定删哪张表,PostgreSQL 只写辅助表
执行前置动作用同样条件先查一次,核对待删行数

典型使用场景

  • 按关联表状态删除主表数据,例如删掉已全额退款的订单
  • 清理无引用记录:删除没有任何明细的主单
  • 按用户维度清理:删除已注销用户的业务数据
  • 归档收尾:确认数据已复制后删除源表记录
  • 级联清理前的预演:先统计影响范围再执行

两库写法与预演

两库写法与预演
-- 先预演:用完全相同的条件统计待删行数
SELECT COUNT(*)
FROM orders o
JOIN refunds r ON r.order_id = o.id
WHERE r.status = 'approved';

-- MySQL:删除关键字后列出要删的别名
DELETE o                       -- 必须写对目标表别名
FROM orders o
JOIN refunds r ON r.order_id = o.id
WHERE r.status = 'approved';

-- PostgreSQL:目标表后跟辅助表清单
DELETE FROM orders o
USING refunds r
WHERE r.order_id = o.id
  AND r.status = 'approved';

-- 包在事务里执行,确认行数符合预期后再提交
-- BEGIN; ... ; COMMIT;

结构对照

对比项MySQLPostgreSQL
要删的表写在删除关键字之后写在删除关键字之后,只写一次
辅助表写在 FROM 里写在 USING 列表里
别名写法必须与 FROM 中的别名一致别名写在目标表之后
写错的后果可能删零行或删错表,均不报错列不存在时直接报错
多表删除可一次删多张表一次只删一张表
MySQL 的多表删除允许一次删多张表,这在级联清理时很方便,但风险也随之放大。建议始终只删一张表,其余表作为连接条件使用。
关联条件不唯一时,两库的删除行为都是「命中即删」,不会因为一对多而多次删除同一行,但预演统计的行数会包含重复计数带来的膨胀,核对时要用去重后的主键数比较。

易错点

  • MySQL 多表删除写错目标表别名,语句成功但删了零行或删错表,且没有任何报错提示
  • 不预演直接执行:删除范围与预期不符时已经没有回滚余地
  • 只删主表不动明细表,或反过来,留下大量孤立数据影响后续关联查询
  • 批量删除不包事务也不分批,长事务持锁期间阻塞线上写入
  • 删除条件里关联字段缺少索引,删除语句执行时间远超预期

数据来源与参考

  • MySQL 8.0 Reference Manual · DELETE
  • PostgreSQL Documentation · DELETE
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌