ID_NO, PHONE) 重複,其他欄位可能不同。DELETE FROM target_table a
WHERE a.ROWID != (
SELECT MAX(b.ROWID)
FROM target_table b
WHERE a.key_col1 = b.key_col1
AND a.key_col2 = b.key_col2
);
-- 1. 建立中間表只存保留的 ROWID
CREATE GLOBAL TEMPORARY TABLE tmp_keep_rowid ON COMMIT PRESERVE ROWS
AS
SELECT MAX(ROWID) AS keep_rid
FROM target_table
GROUP BY key_col1, key_col2
HAVING COUNT(*) > 1;
-- 2. 刪除不在中間表的重複行
DELETE FROM target_table a
WHERE a.ROWID NOT IN (SELECT keep_rid FROM tmp_keep_rowid)
AND EXISTS (
SELECT 1 FROM target_table b
WHERE a.key_col1 = b.key_col1
AND a.key_col2 = b.key_col2
AND b.ROWID IN (SELECT keep_rid FROM tmp_keep_rowid)
);
-- 3. 提交並清理
COMMIT;
優點:避免刪除時重複掃描、減少 UNDO/REDO、可分批提交。
-- 方法 1:CTAS 重建 (需權限、索引/約束/權限需重建)
CREATE TABLE target_table_new AS
SELECT DISTINCT * FROM target_table;
DROP TABLE target_table;
RENAME target_table_new TO target_table;
-- 後續:重建索引、約束、授權、統計資訊
-- 方法 2:臨時表中轉 (保留原表物件屬性)
CREATE GLOBAL TEMPORARY TABLE tmp_dedup ON COMMIT PRESERVE ROWS
AS SELECT DISTINCT * FROM target_table;
DELETE FROM target_table;
INSERT INTO target_table SELECT * FROM tmp_dedup;
COMMIT;
-- 保留最早建立 (假設有 CREATED_AT 欄位)
DELETE FROM target_table a
WHERE a.ROWID != (
SELECT MIN(b.ROWID) KEEP (DENSE_RANK FIRST ORDER BY b.created_at)
FROM target_table b
WHERE a.key_col1 = b.key_col1
AND a.key_col2 = b.key_col2
);
-- 保留狀態 = 'ACTIVE' 優先,其次最新
DELETE FROM target_table a
WHERE a.ROWID != (
SELECT MAX(b.ROWID) KEEP (DENSE_RANK FIRST ORDER BY DECODE(b.status,'ACTIVE',1,2), b.updated_at DESC)
FROM target_table b
WHERE a.key_col1 = b.key_col1
AND a.key_col2 = b.key_col2
);
| 層面 | 措施 |
|---|---|
| 資料庫層 | 加 UNIQUE CONSTRAINT 或 UNIQUE INDEX 於業務鍵 |
| 應用層 | 寫入前 SELECT FOR UPDATE 檢查、或用 MERGE UPSERT、INSERT ... LOG ERRORS |
| ETL/介接層 | 去重邏輯前置、冪等設計、主鍵衝突處理策略 |
| 稽核層 | 定期跑 GROUP BY ... HAVING COUNT(*) > 1 掃描報警 |
DELETE ... WHERE ROWNUM <= 10000 循環,或使用 DBMS_REDEFINITION 線上重定義重建表。