DELETE FROM table_name WHERE rowid NOT IN (SELECT MIN(rowid) FROM table_name GROUP BY col1, col2, col3); 这就是删除重复数据的核心语句,先按重复列分组,取最小rowid保留一条,其他删除。
方案一:使用ROWID
先查重复:select col1,col2,col3,count(*) from table group by col1,col2,col3 having count(*)>1; 然后执行delete from table where rowid not in (select min(rowid) from table group by col1,col2,col3);
方案二:自连接删除
delete from table a where rowid > (select min(rowid) from table b where b.col1=a.col1 and b.col2=a.col2 and b.col3=a.col3);
方案三:使用ROW_NUMBER
先建临时表或直接:delete from table where rowid in (select rid from (select rowid rid, row_number() over(partition by col1,col2 order by rowid) rn from table) where rn>1);
大表优化
对于大表,分批删除:delete from table where rowid in (select rid from (select rowid rid, row_number() over(partition by col1 order by rowid) rn from table where rownum<10000) where rn>1); 循环执行直到无重复。
CTAS重建表
create table new_table as select distinct col1,col2 from old_table; drop table old_table; rename new_table to old_table; 简单高效,避免delete锁表。
注意事项
备份先!commit前检查count(*)变化,用pl/sql块加异常处理,监控undo使用。
FAQ
Q: 删除后数据丢失怎么恢复?
A: 用flashback table table_name to before drop; 或从备份恢复。
Q: 为什么用ROWID不直接用主键?
A: 重复数据主键可能不同,ROWID唯一标识物理行。
Q: 大表删除卡住怎么办?
A: 调大sort_area_size,分批或用CTAS。
Q: Oracle 12c有新方法吗?
A: 用connect by或match_recognize,但ROW_NUMBER最稳。