MySQL 的 DELETE 子查詢不能直接引用同一張表

MySQL 不允許在 DELETE/UPDATE 的子查詢裡讀取正在被改的那張表,會報 You can’t specify target table ‘X’ for update in FROM clause。

-- ❌ MySQL 直接報錯
DELETE FROM Person
WHERE id NOT IN (SELECT MIN(id) FROM Person GROUP BY email);

繞法:多包一層 derived table

外面再包一層 SELECT ... FROM (...) sub,MySQL 會把內層先物化成一張臨時表,就不算「邊讀邊寫同一張表」了:

DELETE FROM Person
WHERE id NOT IN (
    SELECT sub.min_id
    FROM (
        SELECT MIN(id) AS min_id FROM Person GROUP BY email
    ) sub
);

要注意的地方

  • 這是語法層的規避,代價是多一份物化的臨時資料;表大時這層複製不是免費的。
  • PostgreSQL 沒有這個限制,同一句不包 derived table 也能跑——又一個方言差,見 DELETE 搭 JOIN 的寫法 MySQL 與 PostgreSQL 不相容。
  • NOT IN 碰到子查詢結果含 NULL 會整句不成立,這裡因為 MIN(id) 不會是 NULL 才安全;一般情況優先用 NOT EXISTS。