當嘗試建立唯一索引時遇到 ORA-01452: cannot CREATE UNIQUE INDEX; duplicate keys found,表示表中存在重複鍵值。必須先找出並清理重複列,才能成功建立索引。本文介紹三種查找重複資料的經典方法,適用於 Oracle 8i 至最新版本。
Oracle 每列擁有唯一 ROWID,透過自關聯比較業務鍵值,保留 MAX(ROWID) 或 MIN(ROWID) 即可識別重複列:
SELECT a.*
FROM code_ref a
JOIN (SELECT key_col, MAX(rowid) AS keep_rid
FROM code_ref
GROUP BY key_col
HAVING COUNT(*) > 1) b
ON a.key_col = b.key_col
AND a.rowid <> b.keep_rid;
結果即為需刪除的重複列。優點:直觀、不需額外物件;缺點:大表自關聯可能較慢。
最常用、效能最佳的方式:
SELECT key_col, COUNT(*) AS dup_cnt
FROM code_ref
GROUP BY key_col
HAVING COUNT(*) > 1;
若需刪除重複列,可搭配 ROW_NUMBER() 分析函數(Oracle 9i+):
DELETE FROM code_ref
WHERE rowid IN (
SELECT rowid
FROM (
SELECT rowid,
ROW_NUMBER() OVER (PARTITION BY key_col ORDER BY rowid) AS rn
FROM code_ref
)
WHERE rn > 1
);
適用於已有大量資料、需一次性驗證並記錄違規列的場景:
@?/rdbms/admin/utlexcpt.sql 建立 EXCEPTIONS 表(記錄 ROW_ID, OWNER, TABLE_NAME, CONSTRAINT)。ALTER TABLE code_ref ADD CONSTRAINT uk_code_ref UNIQUE (key_col)
EXCEPTIONS INTO exceptions;
SELECT c.*
FROM code_ref c, exceptions e
WHERE c.rowid = e.row_id
AND e.table_name = 'CODE_REF';
注意:utlexcpt.sql 路徑 Windows 為 %ORACLE_HOME%\rdbms\admin,Linux/Unix 為 $ORACLE_HOME/rdbms/admin。
DROP 舊表改名,避免大量 DELETE 產生 UNDO/REDO 壓力。ORA-01452 會阻擋索引建立。掌握這三招,面對「無法建立唯一索引」的報錯即可從容應對,快速定位並清理髒資料,確保資料品質與索引順利上線。