表中存在重複行(業務主鍵重複),需**刪除多餘副本,保留 1 條**(通常保留 ROWID 最小/最大、或最早/最新創建時間)。
DELETE FROM t_target
WHERE rowid NOT IN (
SELECT MIN(rowid) FROM t_target GROUP BY id HAVING COUNT(*) > 1
)
AND id IN (SELECT id FROM t_target GROUP BY id HAVING COUNT(*) > 1);
DELETE FROM t_target a
WHERE (a.col1, a.col2) IN (
SELECT col1, col2 FROM t_target GROUP BY col1, col2 HAVING COUNT(*) > 1
)
AND rowid NOT IN (
SELECT MIN(rowid) FROM t_target GROUP BY col1, col2 HAVING COUNT(*) > 1
);
DELETE FROM t_target
WHERE rowid IN (
SELECT rowid FROM (
SELECT rowid,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY create_time DESC) AS rn
FROM t_target
) WHERE rn > 1
);
優勢:單次全表掃描、可靈活指定保留策略(最新/最舊/最大 ID)、並行執行友好。
12c+ 簡化:DELETE FROM t_target WHERE ROW_NUMBER() OVER (...) > 1(尚不直接支持,需子查詢)。
MERGE INTO t_target t
USING (
SELECT rowid AS rid,
ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY create_time) AS rn
FROM t_target
) s
ON (t.rowid = s.rid)
WHEN MATCHED THEN UPDATE SET t.dup_flag = CASE WHEN s.rn > 1 THEN 'Y' ELSE 'N' END
-- 後續: DELETE FROM t_target WHERE dup_flag = 'Y';
CREATE TABLE t_target_dedup NOLOGGING PARALLEL 8 AS
SELECT * FROM (
SELECT t.*, ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY create_time) rn
FROM t_target t
) WHERE rn = 1;
-- 索引/約束/權限/同義詞/觸發器 重建
DROP TABLE t_target;
ALTER TABLE t_target_dedup RENAME TO t_target;
| 方案 | 耗時 | Redo/Undo | 鎖持有 | 適用場景 |
|---|---|---|---|---|
| ROWID 子查詢 | 42 分鐘 | 高 | 行級鎖長 | < 1000 萬行、重複率低 |
| ROW_NUMBER() | 18 分鐘 | 中 | 行級鎖短 | 常規大表、需靈活保留策略 |
| CTAS 重建 | 6 分鐘 | 極低 (NOLOGGING) | 無 DML 鎖 | > 5000 萬行、需整理碎片、維護窗口充足 |
ALTER SESSION ENABLE PARALLEL DML + Hint PARALLEL(8)。中小表用 ROW_NUMBER() 單語句優雅去重;超大表用 CTAS NOLOGGING PARALLEL 重建表極速去重並整理碎片;業務主鍵與保留策略必須先行確認。