SELECT col1, col2, COUNT(*)
FROM table_name
GROUP BY col1, col2
HAVING COUNT(*) > 1;
DELETE FROM table_name a
WHERE (col1, col2) IN (
SELECT col1, col2 FROM table_name
GROUP BY col1, col2 HAVING COUNT(*) > 1
);
⚠️ 子查詢執行計劃差,大表易導致全表掃描鎖表、UNDO 爆漲。
-- 步驟 1:創建臨時表存儲重複鍵值
CREATE TABLE tmp_dup_keys AS
SELECT col1, col2
FROM table_name
GROUP BY col1, col2
HAVING COUNT(*) > 1;
-- 步驟 2:建立索引加速連接
CREATE INDEX idx_tmp_dup ON tmp_dup_keys(col1, col2);
-- 步驟 3:高效刪除
DELETE FROM table_name a
WHERE EXISTS (
SELECT 1 FROM tmp_dup_keys b
WHERE a.col1 = b.col1 AND a.col2 = b.col2
);
-- 步驟 4:提交並清理
COMMIT;
DROP TABLE tmp_dup_keys;
Oracle 隱藏列 ROWID 唯一標識每行,值越大越新(一般情況)。
-- 查詢重複行(保留 ROWID 最大者)
SELECT a.rowid, a.*
FROM table_name a
WHERE a.rowid != (
SELECT MAX(b.rowid)
FROM table_name b
WHERE a.col1 = b.col1 AND a.col2 = b.col2
);
-- 刪除重複,保留最新
DELETE FROM table_name a
WHERE a.rowid != (
SELECT MAX(b.rowid)
FROM table_name b
WHERE a.col1 = b.col1 AND a.col2 = b.col2
);
CREATE TABLE tmp_keep AS
SELECT col1, col2, MAX(rowid) AS keep_rowid
FROM table_name
GROUP BY col1, col2;
DELETE FROM table_name a
WHERE a.rowid != (
SELECT b.keep_rowid FROM tmp_keep b
WHERE a.col1 = b.col1 AND a.col2 = b.col2
);
COMMIT;
DROP TABLE tmp_keep;
-- 1. 創建去重臨時表
CREATE TABLE tmp_dedup AS SELECT DISTINCT * FROM table_name;
-- 2. 刪除原表(或 TRUNCATE 保留結構/權限/索引)
DROP TABLE table_name;
-- 或 TRUNCATE TABLE table_name; -- 需先禁用外鍵
-- 3. 重建表並導入數據
CREATE TABLE table_name AS SELECT * FROM tmp_dedup;
-- 重建索引、約束、授權、觸發器...
-- 4. 清理
DROP TABLE tmp_dedup;
CREATE TABLE tmp_dedup AS SELECT DISTINCT * FROM table_name;
TRUNCATE TABLE table_name;
INSERT INTO table_name SELECT * FROM tmp_dedup;
COMMIT;
DROP TABLE tmp_dedup;
| 數據量 | 重複率 | 推薦方案 | 關鍵點 |
|---|---|---|---|
| < 10 萬 | 任意 | 直接 DELETE / DISTINCT 重建 | 簡單直觀 |
| 10 萬 - 1000 萬 | 低 (<5%) | 臨時表 + EXISTS/ROWID | 索引加速,分批提交 |
| > 1000 萬 | 高 (>10%) | CTAS 重建 + 並行 DDL | CREATE TABLE ... PARALLEL 8 AS SELECT DISTINCT... |
| 極大表 | 需業務主鍵去重 | 分區交換 / DBMS_REDEFINITION | 在線重定義,零停機 |
LOOP
DELETE FROM table_name a
WHERE a.rowid IN (
SELECT rowid FROM (
SELECT rowid FROM table_name a
WHERE a.rowid != (SELECT MAX(rowid) FROM table_name b WHERE a.col1=b.col1 AND a.col2=b.col2)
AND ROWNUM <= 10000
)
);
EXIT WHEN SQL%ROWCOUNT = 0;
COMMIT;
END LOOP;
CREATE TABLE new_t PARALLEL 8 AS SELECT DISTINCT * FROM old_t;ALTER TABLE t ADD CONSTRAINT uk_t UNIQUE (col1,col2) EXCEPTIONS INTO exceptions;,再處理 exceptions 表DISTINCT 重建,大表用 ROWID + 臨時表MAX(ROWID)