經常有這樣一個業務場景,我們在同一張表中有重復的記錄,我們要刪除重復的記錄,這篇文章就是用來解決這個問題的,用的方法是我目前遇到的效率最高的方法(如果有更好的方法歡迎網友提供)。這個方法會用到了rowid,下面簡單介紹一下rowid的定義
1、ROWID定義
ROWID:數據庫中行的全局唯一地址
對於數據中的每一行,rowid偽列返回行的地址。rowid值主要包含以下信息:
- 對象的數據對象編號
- 該行所在的數據文件中的數據塊
- 該行中數據塊的位置(第一行是0)
- 數據行所在的數據文件(第一個文件是1)。該文件編號是相對於表空間。
通常來說,一個rowid值唯一標識數據中的一行。然而,存儲在同一聚簇中不同的表可以有相同的rowid。
2、實現的方法
方法一:
DELETE FROM hr.employees WHERE ROWID IN ( SELECT ROWID FROM ( SELECT first_name, last_name, ROWID, ROW_NUMBER() OVER(PARTITION BY first_name,last_name ORDER BY employee_id) AS staff_row --按照保留的唯一字段進行分區,取row_number FROM hr.employees ) WHERE staff_row > 1 );
乍一看,我勒個去,這個是什么東西要,這么難懂!!沒關系,下面給您解釋一下,就很好懂了。
針對的是oracle內置的示例用戶hr中的employees這張表,我們希望的是first_name和last_name沒有重復的項(如果你的業務需要時別的話可以相應的轉換,如在成績表中的學號就是唯一個,就partition by學號),所以對這兩個字段進行partition by。
在子查詢的子查詢中我們選擇的主要目的是rowid和row_number(first_name和last_name只是用來輔助理解加進去的字段);子查詢中我們選擇了row_number > 1的rowid,這樣的話按照first_name和last_name分組中每一個分組只有一條記錄沒有被選擇;最外面的delete就直接把選擇出來的rowid進行了刪除。至此完成了hr.employees對於first_name和last_name的去重。
有朋友會說,媽蛋這太難理解了吧!在這種情況下,往往有方法二~~~
方法二:
DELETE FROM hr.employees t1 WHERE t1.ROWID NOT IN ( SELECT MIN(t2.ROWID) FROM hr.employees t2 GROUP BY t2.employee_id --按照想要唯一保留的字段進行分組 );
這個明顯就比方法一好多了,子查詢中我們先選除了rowid,然后按照我們想要保留的唯一字段進行分組,並取每組最小的rowid(注意是子查詢表的rowid);然后在用not in刪除除開最小的rowid以外的所有記錄。
怎么樣,這個方法是不是瞬間解決並且非常好理解?但是你以為這樣就結束了?no no no
方法三:
DELETE FROM hr.employees t1 WHERE t1.rowid > ( SELECT MIN(t2.rowid) FROM hr.employees t2 WHERE t1.employee_id = t2.employee_id --按照想要唯一保留的字段進行匹配 );
這個方式看起來和方法二差不多,但是想要說的是,他用的是連接,他用的是連接,不敢說連接一定比group by快,但是基本上不會輸group by,而且在一般的情況下也是最快的了。而且外層的">"可以用到索引,就是各種快。
方法也同樣說一下,子查詢中按照要保留的字段對t1和t2進行關聯,然后選擇出最小的rowid(注意是子查詢表的rowid),然后在外層用">"只保留每個匹配結果最小的一條記錄。然后就瞬間刪除重復的記錄
此時有朋友以為這是最快的辦法了,但是,我想說,不是,不是!請看下面最快的方法!

--騙一下你的,上面已經是本人接觸到的最快的方法了