MySQL 禁止在同一語句中先 SELECT 同表數據,再 UPDATE/DELETE 該表。Oracle/SQL Server 無此限制。報錯示例:
DELETE FROM tbl
WHERE id IN (
SELECT MAX(id) FROM tbl a WHERE EXISTS (
SELECT 1 FROM tbl b WHERE a.tac = b.tac GROUP BY tac HAVING COUNT(1) > 1
)
GROUP BY tac
);
報錯:ERROR 1093 (HY000): You can't specify target table 'tbl' for update in FROM clause
DELETE FROM tbl
WHERE id IN (
SELECT a.id FROM (
SELECT MAX(id) AS id FROM tbl a WHERE EXISTS (
SELECT 1 FROM tbl b WHERE a.tac = b.tac GROUP BY tac HAVING COUNT(1) > 1
)
GROUP BY tac
) a
);
SELECT ... FROM (subquery) alias 強制生成派生表(物化臨時表),打破直接引用鏈,繞過限制。| 操作 | 是否觸發 1093 | 解決 |
|---|---|---|
DELETE ... WHERE col IN (SELECT ... FROM same_table) | 是 | 包裹派生表 |
UPDATE ... SET col = (SELECT ... FROM same_table) | 是 | 包裹派生表 / 多表 UPDATE 語法 |
INSERT ... SELECT ... FROM same_table | 否 | 無需處理 |
REPLACE ... SELECT ... FROM same_table | 否 | 無需處理 |
-- 刪除重複 tac 組中非最大 id
DELETE t1 FROM tbl t1
JOIN (
SELECT tac, MAX(id) AS max_id FROM tbl GROUP BY tac HAVING COUNT(*) > 1
) t2 ON t1.tac = t2.tac AND t1.id < t2.max_id;
多表語法天然避開 1093,且可利用 JOIN 索引,性能通常優於子查詢。
MySQL 1093 是保護機制而非缺陷。派生表包裹是通用繞過法;多表 JOIN 寫法更地道、更快。遇到即改,養成肌肉記憶。