MySQL DELETE JOIN必须显式指定目标表别名,正确格式为DELETE t1 FROM t1 JOIN t2 ON条件 WHERE子句;错误写法DELETE FROM t1 JOIN t2会报ERROR 1064,只删主表时目标别名须紧随DELETE后,多表删除需用DELETE t1, t2 FROM t1 JOIN t2 ON。

DELETE JOIN语法必须写对表顺序和别名
MySQL不支持标准SQL的DELETE FROM t1 JOIN t2写法,直接这么写会报ERROR 1064。正确结构是把JOIN放在FROM子句里,且只允许删FROM后第一个表(或显式列出的表)。
常见错误写法:DELETE FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.status = 'inactive' —— 这根本不能执行。
正确写法分两种场景:
- 只删主表:用
DELETE o FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.status = 'inactive' - 同时删多表:用
DELETE o, c FROM orders o JOIN customers c ON o.customer_id = c.id WHERE o.created_at ,注意<code>o, c必须写在DELETE后,且只能删这些明确列出的表
别名不是可选的——没别名就无法在ON和WHERE里准确引用字段,尤其多表时容易歧义。
INNER JOIN vs LEFT JOIN:删交集还是删孤立数据
INNER JOIN只删两表都存在的匹配行;LEFT JOIN ... WHERE xxx IS NULL才用来删“孤儿”记录(比如没订单的客户、没主表记录的明细)。
典型误用:想删所有已注销客户的订单,却写了DELETE o FROM orders o INNER JOIN customers c ON o.customer_id = c.id WHERE c.status = 'inactive'——这没问题;但若想删“所有没对应客户的订单”,就必须换LEFT JOIN:
DELETE o FROM orders o LEFT JOIN customers c ON o.customer_id = c.id WHERE c.id IS NULL
注意WHERE条件必须作用于右表的字段(如c.id IS NULL),否则LEFT JOIN退化成INNER JOIN,起不到找孤立数据的作用。
没索引的JOIN条件会让DELETE慢到超时甚至锁库
JOIN字段没索引,MySQL就得对每行做嵌套循环全表扫描。10万行 × 10万行 = 100亿次比对——你看到的不是“删得慢”,是连接被kill,或主库卡死数小时。
检查方式很简单:EXPLAIN SELECT * FROM orders o JOIN customers c ON o.customer_id = c.id WHERE c.status = 'inactive',重点看:
-
type列是否为ref或eq_ref(而不是ALL) -
key列是否显示用了索引(比如customer_id)
关键点:不是主表主键有索引就够了,关联字段(如orders.customer_id)必须单独建索引。否则JOIN效率崩盘,DELETE变成高危操作。
删完不等于空间释放,大表必须OPTIMIZE
DELETE只是标记删除,InnoDB不会立刻回收磁盘空间。你看到COUNT(*)变少,但du -sh查文件大小纹丝不动,buffer pool里还堆着旧版本页,MVCC链变长,后续查询可能更慢。
处理方式:
- 小表:立刻跑
OPTIMIZE TABLE orders - 中大表:用
ALTER TABLE orders ENGINE=InnoDB(语义更清晰,避免OPTIMIZE在某些版本的隐式行为)
别在业务高峰期跑这些命令——它们会触发大量IO、锁表、刷脏页,监控innodb_buffer_pool_wait_free飙升就是信号。真正要缩文件大小的,才是OPTIMIZE或ALTER TABLE该干的事;日常清理死亡元组,靠的是定期autocommit事务+正常写入压力下的后台purge线程。


















