表 schools 中 school_name 存在重複,需刪除重複行,保留 school_id 最小或最大的一條。
school_id | school_name | total_student | test_takers ----------|--------------------------|---------------|------------ 1239 | Abraham Lincoln High | 55 | 50 1240 | Abraham Lincoln High | 70 | 35 ← 重複 1241 | Acalanes High | 120 | 89 1242 | Academy Of The Canyons | 30 | 30 1243 | Agoura High | 89 | 40 1244 | Agoura High | 100 | 50 ← 重複
DELETE t1
FROM `test` t1
INNER JOIN (
SELECT school_name, MIN(school_id) AS min_id
FROM `test`
GROUP BY school_name
HAVING COUNT(*) > 1
) t2 ON t1.school_name = t2.school_name AND t1.school_id > t2.min_id;
school_name 及其最小 school_idINNER JOIN 語法,MySQL 支持多表刪除,性能優於子查詢 INDELETE t1
FROM `test` t1
INNER JOIN (
SELECT school_name, MAX(school_id) AS max_id
FROM `test`
GROUP BY school_name
HAVING COUNT(*) > 1
) t2 ON t1.school_name = t2.school_name AND t1.school_id < t2.max_id;
-- 保留每組 row_number = 1 的行(MySQL 8.0+)
WITH ranked AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY school_name ORDER BY school_id ASC) AS rn
FROM `test`
)
DELETE FROM `test`
WHERE school_id IN (SELECT school_id FROM ranked WHERE rn > 1);
CREATE TABLE `test_dedup` LIKE `test`;
INSERT INTO `test_dedup`
SELECT * FROM `test`
WHERE school_id IN (
SELECT MIN(school_id) FROM `test` GROUP BY school_name
);
RENAME TABLE `test` TO `test_backup`, `test_dedup` TO `test`;
| 方案 | 適用量級 | 鎖表影響 | 優點 |
|---|---|---|---|
| JOIN DELETE | < 10 萬行 | 行鎖 | 單條語句,無需建表 |
| CTE + 窗口函數 | MySQL 8.0+ | 行鎖 | 邏輯清晰,支持複雜排序 |
| 新表插入 + RENAME | > 10 萬行 | 極短元數據鎖 | 不阻塞讀寫,最安全 |
ALTER TABLE test ADD UNIQUE KEY uk_name (school_name);-- 檢查是否還有重複 SELECT school_name, COUNT(*) FROM `test` GROUP BY school_name HAVING COUNT(*) > 1; -- 對比行數 SELECT COUNT(*) FROM `test`; SELECT COUNT(DISTINCT school_name) FROM `test`;