用Oracle的分析函數刪除重複的數據

用Oracle的分析函數刪除重複的數據

沒有主鍵(Primary Key)約束保護的表格可能會讓重複的數據行被插入進來。查找這種重複數據的傳統方式是通過GROUP BY和HAVING關鍵字進行查詢。在根據關鍵列把數據分組並計算每個組裏的行數之後,有一個以上成員的組就是帶有重複數據的組。

 

     儘管發現這樣的數據行很容易,但是解決這一問題卻十分耗時。在Oracle裏,獨特的ROWID僞列(pseudocolumn)意味着沒有兩個列是真正一模一樣的。你可以總是利用刪除(DELETE)查詢來參考一個以外的所有ROWID,以便刪除所有的重複數據。這非常有效——如果你沒有太多的重複數據需要刪除的話。而Oracle 9i裏引入的分析函數給予了我們一種更簡單的方式來進行這種清除工作。

  ROW_NUMBER()分析函數與ROWNUM僞列相似的地方在於它們都能夠給輸出的行編號。但是ROWNUM給出的是整個數據列完整的序列,而ROW_NUMBER會在我們在數據列裏定義的每個分區裏把編號重新設置回1。這樣做的結果是不僅能夠很容易就看到哪個組裏有多個成員,還能夠確切知道需要刪除哪個行。

  分析查詢的格式是:

  Functionname (arguments) OVER (PARTITION BY columns ORDER BY columns)

  現在讓我們假設在創建SCOTT.EMP表格副本的時候出現了錯誤,所有的行都被輸入了兩遍。嘗試加入一個主鍵約束會失敗,因爲數據已經出現了重複。列表A顯示了這一過程,爲了清楚說明問題,它被分成兩個階段:

  Listing A SQL> -- Will you just LOOK at this table? Lots of duplicates!

  SQL>

  SQL> SELECT empno, ename

  FROM emp2

  ORDER BY empno;

 

  EMPNO ENAME

  ---------- ----------

  7369 SMITH

  7369 SMITH

  7499 ALLEN

  7499 ALLEN

  7521 WARD

  7521 WARD

  7566 JONES

  7566 JONES

  7654 MARTIN

  7654 MARTIN

  7698 BLAKE

  7698 BLAKE

  7782 CLARK

  7782 CLARK

  7788 SCOTT

  7788 SCOTT

  7839 KING

  7839 KING

  7844 TURNER

  7844 TURNER

  7876 ADAMS

  7876 ADAMS

  7900 JAMES

  7900 JAMES

  7902 FORD

  7902 FORD

  7934 MILLER

  7934 MILLER

  28 rows selected.

  SQL> -- First step: number the duplicates of each empno

  SQL>

  SQL> SELECT ROWID, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) rn

  FROM emp2;

 

  ROWID RN

  ------------------ ----------

  AAAM1UAAEAAAAGsAAA 1

  AAAM1UAAEAAAAGuAAA 2

  AAAM1UAAEAAAAGuAAB 1

  AAAM1UAAEAAAAGsAAB 2

  AAAM1UAAEAAAAGsAAC 1

  AAAM1UAAEAAAAGuAAC 2

  AAAM1UAAEAAAAGuAAD 1

  AAAM1UAAEAAAAGsAAD 2

  AAAM1UAAEAAAAGsAAE 1

  AAAM1UAAEAAAAGuAAE 2

  AAAM1UAAEAAAAGsAAF 1

  AAAM1UAAEAAAAGuAAF 2

  AAAM1UAAEAAAAGsAAG 1

  AAAM1UAAEAAAAGuAAG 2

  AAAM1UAAEAAAAGsAAH 1

  AAAM1UAAEAAAAGuAAH 2

  AAAM1UAAEAAAAGsAAI 1

  AAAM1UAAEAAAAGuAAI 2

  AAAM1UAAEAAAAGsAAJ 1

  AAAM1UAAEAAAAGuAAJ 2

  AAAM1UAAEAAAAGsAAK 1

  AAAM1UAAEAAAAGuAAK 2

  AAAM1UAAEAAAAGsAAL 1

  AAAM1UAAEAAAAGuAAL 2

  AAAM1UAAEAAAAGsAAM 1

  AAAM1UAAEAAAAGuAAM 2

  AAAM1UAAEAAAAGuAAN 1

  AAAM1UAAEAAAAGsAAN 2

  28 rows selected.

  SQL> -- Now, use that as an inline view, and select just the dups

  SQL> -- We're including the row number, it won't be in the final query

  SQL>

  SQL> SELECT ROWID, rn

  FROM

  (SELECT ROWID, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) rn

  FROM emp2)

  WHERE rn > 1;

 

  ROWID RN

  ------------------ ----------

  AAAM1UAAEAAAAGuAAA 2

  AAAM1UAAEAAAAGsAAB 2

  AAAM1UAAEAAAAGuAAC 2

  AAAM1UAAEAAAAGsAAD 2

  AAAM1UAAEAAAAGuAAE 2

  AAAM1UAAEAAAAGuAAF 2

  AAAM1UAAEAAAAGuAAG 2

  AAAM1UAAEAAAAGuAAH 2

  AAAM1UAAEAAAAGuAAI 2

  AAAM1UAAEAAAAGuAAJ 2

  AAAM1UAAEAAAAGuAAK 2

  AAAM1UAAEAAAAGuAAL 2

  AAAM1UAAEAAAAGuAAM 2

  AAAM1UAAEAAAAGsAAN 2

  14 rows selected.

  SQL> -- Now we DELETE all the rows in that set

  SQL>

  SQL> DELETE FROM emp2

  WHERE ROWID IN

  (SELECT ROWID

  FROM (SELECT ROWID,

  ROW_NUMBER() OVER (PARTITION BY empno ORDER BY EMPNO) rn

  FROM emp2)

  WHERE rn > 1);

 

  14 rows deleted.

  SQL> commit;

  Commit complete.

  SQL> -- Show the de-dup'ed table

  SQL>

  SQL> SELECT empno, ename

  FROM emp2;

 

  EMPNO ENAME

  ---------- ----------

  7369 SMITH

  7521 WARD

  7654 MARTIN

  7698 BLAKE

  7782 CLARK

  7788 SCOTT

  7839 KING

  7844 TURNER

  7876 ADAMS

  7900 JAMES

  7902 FORD

  7499 ALLEN

  7566 JONES

  7934 MILLER

  首先是一個分析查詢,通過empno行來分區;它使用ROW_NUMBER()給每個分區進行編號。如果沒有重複的內容,分區就只有一個行,編號是“1”。但是,如果存在重複,那麼它們就會被編上2、3等號碼。這個查詢還會返回我們用來唯一識別數據行的ROWID。第一個查詢然後就被用作另外一個查詢的內聯視圖,這第二個查詢使用一個WHERE子句過濾掉“1”行,只返回重複的內容。最後,一個DELETE語句通過第二個查詢使用IN操作符來刪掉所有的重複內容。

  就和所有的大規模DELETE一樣,你需要記住的是,最好把想要保留的行(也就是說那些ROW_NUMBER爲1的行)保存到一個新的表格裏。INSERT所造成的負載要比DELETE小得多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章