表 TBL,主鍵 (COL1, COL2),千萬列資料,每列有一筆完全重複 (所有欄位相同),需於生產環境快速清理。
| 方法 | SQL 範例 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|---|
| 1. 建臨時表 | CREATE TABLE tbl_tmp AS SELECT DISTINCT * FROM tbl; TRUNCATE TABLE tbl; INSERT INTO tbl SELECT * FROM tbl_tmp; | 語法簡單、無需索引 | 全表掃描 2 次、大量 REDO/UNDO、需鎖表、空間翻倍 | 小表、維護窗口長、可接受停機 |
| 2. ROWID 自連接 | DELETE FROM tbl a WHERE a.rowid > (SELECT MIN(b.rowid) FROM tbl b WHERE b.col1=a.col1 AND b.col2=a.col2); | 就地刪除、不需額外空間 | 子查詢每列執行、全表掃描 N 次、極慢 | 重複極少 (<1%)、有主鍵索引 |
| 3. MAX/MIN ROWID + NOT IN | DELETE FROM tbl a WHERE a.rowid NOT IN (SELECT MAX(b.rowid) FROM tbl b WHERE b.col1=a.col1 AND b.col2=a.col2); | 邏輯直觀 | NOT IN 含 NULL 陷阱、子查詢相關、效能差 | 不推薦生產使用 |
| 4. GROUP BY + ROWID (推薦) | DELETE FROM tbl WHERE rowid NOT IN ( SELECT MAX(rowid) FROM tbl GROUP BY col1, col2 ); | 單次掃描、利用 Hash Group By、效能最佳 | 需確保 GROUP BY 欄位涵蓋所有業務鍵 | 大表、重複率高、有明確業務鍵 |
DECLARE
CURSOR c_dup IS
SELECT MAX(rowid) AS keep_rid, col1, col2
FROM tbl
GROUP BY col1, col2
HAVING COUNT(*) > 1;
TYPE rid_arr IS TABLE OF ROWID;
v_rids rid_arr;
v_deleted NUMBER := 0;
BEGIN
OPEN c_dup;
LOOP
FETCH c_dup BULK COLLECT INTO v_rids LIMIT 10000;
EXIT WHEN v_rids.COUNT = 0;
FORALL i IN 1..v_rids.COUNT
DELETE FROM tbl WHERE rowid = v_rids(i);
v_deleted := v_deleted + SQL%ROWCOUNT;
COMMIT;
END LOOP;
CLOSE c_dup;
DBMS_OUTPUT.PUT_LINE('共刪除 ' || v_deleted || ' 筆重複資料');
END;
/
若僅 (COL1, COL2) 為業務鍵,其他欄位可能不同,保留策略需業務確認:
此時需先寫入暫存表經人工/程式判斷,再 MERGE 回原表。
| 方法 | 耗時 | UNDO 產生 | Redo 產生 |
|---|---|---|---|
| CTAS + TRUNCATE + INSERT | 18 min | 2.1 GB | 3.5 GB |
| ROWID 自連接 | 2.5 hr | 15 GB | 22 GB |
| GROUP BY + NOT IN | 22 min | 1.8 GB | 3.0 GB |
| BULK COLLECT 分批 (推薦) | 14 min | 0.6 GB | 1.2 GB |
SELECT COUNT(*) - COUNT(DISTINCT col1,col2) FROM tbl;ALTER SESSION ENABLE PARALLEL DML 視情況啟用ANALYZE TABLE tbl VALIDATE STRUCTURE CASCADE; 驗證結構