mysql多表删除必须用delete t1 from t1 join t2格式,不支持标准sql的using语法;去重删重复行需用inner join自关联并设t1.id>t2.id条件;复杂逻辑推荐left join子查询方式;大数据量删除须加索引和where过滤。

DELETE + JOIN 语法必须写成 DELETE t1 FROM t1 JOIN t2 形式
MySQL 不支持标准 SQL 的 DELETE FROM t1 USING t1 JOIN t2 写法,直接写 DELETE FROM t1 INNER JOIN t2 ON ... 会报错 ERROR 1064。正确语法必须显式指定要删的表别名,并放在 DELETE 后、FROM 前:
DELETE t1 FROM table1 t1 INNER JOIN table2 t2 ON t1.id = t2.id WHERE ...- 不能省略
t1(即目标表别名),也不能把FROM放在DELETE前面 - 多表删除时,可同时指定多个别名:
DELETE t1, t2 FROM t1 LEFT JOIN t2 ON t1.id = t2.ref_id
删除重复行:保留最小 id,用 INNER JOIN 自关联
典型场景是同一张表里按业务字段(如 name, email)去重,只留 id 最小的那条。核心思路是让每条“该删的记录”都能在自连接中找到一个 id 更小、但业务字段相同的搭档:
DELETE t1 FROM stu t1 INNER JOIN stu t2 ON t1.name = t2.name AND t1.id > t2.id- 这里
t1.id > t2.id是关键:它确保t1是重复组里 id 较大的那些行 - 如果业务字段有多个(比如
name和age都要一致),条件要写全:ON t1.name = t2.name AND t1.age = t2.age AND t1.id > t2.id - 执行前务必加
SELECT t1.* FROM stu t1 INNER JOIN stu t2 ON ...预览将删哪些行
LEFT JOIN + 子查询:更安全的去重删除方式
当重复逻辑复杂(比如要保留最大 id,或按时间字段选最新记录),用子查询先找出“要保留的 id”,再用 LEFT JOIN 反向匹配出“要删的”更清晰、不易出错:
DELETE t1 FROM uf_cs_record_batch_detail t1 LEFT JOIN (SELECT MIN(id) AS keep_id FROM uf_cs_record_batch_detail GROUP BY cs_contact_name, cs_safe_remark) t2 ON t1.id = t2.keep_id WHERE t2.keep_id IS NULL- 子查询
GROUP BY定义去重维度,MIN(id)或MAX(id)控制保留策略 -
LEFT JOIN后WHERE t2.keep_id IS NULL表示“没被选中保留”的所有行 - 比纯
INNER JOIN自关联更容易理解,也方便加索引优化(例如在(cs_contact_name, cs_safe_remark)上建联合索引)
性能和风险:500万行以上必须注意的事
直接 DELETE JOIN 在大数据量下可能锁表久、慢、甚至 OOM。不是语法对了就能放心跑:
- 没有
WHERE条件或条件未命中索引时,MySQL 会扫描全表构建连接结果集——3000万行可能卡死 - 建议先在
WHERE中加粗粒度过滤,例如:AND t1.create_time - 生产环境务必开启事务并测试回滚:
BEGIN; DELETE ...; SELECT ROW_COUNT(); ROLLBACK; - 如果单次删除超 10 万行,拆成
WHERE id BETWEEN x AND y分批执行,避免长事务阻塞
真正容易被忽略的,是 delete 操作本身不会触发外键级联,也不会走触发器——它只删你明确写出的表。如果业务上依赖其他表的同步清理,得另外处理。











