Oracle中批量刪除數據

在一個ORACLE數據庫運行過程中,有時候會遇到要批量刪除數據的情況,如一個保存歷史數據的表中有大量的數據已經不需要保留,要將這部分數據刪除。以下就這種情況可以採取的方法進行討論。
 
1、使用TRUNCATE命令進行刪除。
如果是整個表的數據都要刪除的話,使用TRUNCATE TABLE命令是理想的選擇。它刪除了表中的所有數據,並且因爲不寫REDO LOG FILE,所以速度很快。刪除的同時,表的索引和約束條件仍然存在。這種方法適用於ORACLE的各個版本。但是當要刪除的數據只是表中的一部分時,這種方法便行不通了。
 
2、直接進行DELETE操作。
直接使用DELETE命令進行刪除,如果刪除的數據量較大時,可能導致回滾段出錯。這是因爲在刪除數據的過程中,不斷擴展回滾段,直到回滾段的最大範圍數或回滾段所在表空間空閒空間用完而出錯。解決這個問題可以通過給刪除數據的事務指定一個足夠大的回滾段或者將回滾段所在表空間的AUTOEXTEND選項打開,同時將回滾段的MAXEXTENTS改大或設爲UNLIMITED。不過這樣仍存在一個隱患,如果刪除的數據量大,同時數據庫工作于歸檔模式下時,有可能導致日誌切換頻繁,所有日誌文件都處於需要歸檔的狀況,而歸檔進程來不及歸檔日誌文件的情況出現,這時數據庫將被掛起,直到有可用的日誌文件後才恢復正常。
所以這種方法也不理想。
 
3、通過PL/SQL循環分段刪除。
第三種方法是專門針對上面第二種方法進行優化改進的。這種方法通過一段PL/SQL程序循環分段刪除數據,逐步提交事務,達到縮小事務規模,安全刪除數據的目的。 
例如有一個數據表t_table,我們將對其中字段c_date滿足小於2001年1月1日的記錄進行刪除,可以採用以下的PL/SQL程序。
 
1 DECLARE
2 V_TEMP NUMBER;
3 BEGIN
4 LOOP
5 BEGIN
6 SELECT 1 INTO V_TEMP FROM t_table WHERE c_date < to_date('2000/01/01','yyyy/mm/dd') AND rownum = 1;
7 DELETE FROM t_table WHERE c_date < to_date('2000/01/01','yyyy/mm/dd') AND rownum < 100;
8 COMMIT;
9 EXCEPTION 
10 WHEN NO_DATA_FOUND THEN
11 EXIT;
12 END;
13 END LOOP;
14 END;
 
程序的第1和第2行聲明瞭一個臨時變量。第4到第13行定義了一個循環,在這個循環中第6行不斷檢查表中是否還有滿足條件的記錄,如果有,第7行程序便執行刪除操作,每次刪除100記錄,同時提交事務。當表中已無滿足條件的記錄時,便引起NO_DATA_FOUND的異常,從而退出循環。通過分批刪除,逐步提交,縮小了事務的規模,從而達到避免出現回滾段錯誤的目的。然而這種方法依然存在因日誌切換頻繁,而歸檔進程來不及歸檔日誌文件而導致數據庫掛起的可能性。下面的程序通過ORACLE所提供的dbms_lock包中的過程sleep,解決了這個問題,從而達到安全快速大量刪除數據的目的。
 
1 DECLARE
2 V_LOGNUM NUMBER; -- 數據庫中擁有的日誌文件數
3 V_NEEDARC NUMBER; -- 需要歸檔的日誌文件數
4 BEGIN
5 SELECT count(1) INTO V_LOGNUM FROM V$LOG;
6 LOOP
7 LOOP
8 SELECT count(1) INTO V_NEEDARC FROM V$ARCHIVE;
9 IF V_NEEDARC < V_LOGNUM - 1 THEN
10 EXIT;
11 ELSE
12 DBMS_LOCK.SLEEP(60);
13 END IF;
14 END LOOP;
15
16 DELETE FROM t_table WHERE c_date < to_date('2000/01/01','yyyy/mm/dd') AND rownum < 100;
17 IF SQL%ROWCOUNT = 0 THEN
18 EXIT;
19 END IF;
20 COMMIT;
21 END LOOP;
22 END;
程序中的第2和第3行聲明瞭兩個變量v_lognum和v_needarc來保存數據庫中日誌文件的數量和當前需要歸檔的日誌文件數量。
第5行獲取了數據庫中日誌文件的數量。
第6行到第21行開始了刪除數據的循環,第7行到第14行是一個子循環,不斷檢測當前需要歸檔的日誌文件的數量v_needarc是否小於數據庫的日誌文件總數v_lognum減去1,如果滿足條件,則退出子循環,開始刪除數據。否則的話便調用dbms_lock.sleep()過程,使程序休眠60秒,然後繼續子循環,檢測需歸檔的日誌文件數量。
第17到19行,檢查刪除數據的結果,如果已無數據,則退出,程序結束。
這個程序,通過利用dbms_output.sleep()過程,在刪除過程中當需要歸檔的日誌文件達到認定的限制時,使刪除過程暫時停止,等待ARCH進程將日誌文件歸檔後再繼續進行,從而達到避免歸檔日誌文件來不及歸檔,導致數據庫掛起的問題。
此方法適用於oracle的各個版本。
 
4、使用NOLOGGING選項重新建表。 
在ORACLE 8以後的版本中,CREATE TABLE命令提供了NOLOGGING的選項,在建表時不用寫日誌文件。
這樣當我們在刪除大量的數據時可以將要保留的數據通過CREATE TABLE ... NOLOGGING ... AS SELECT * FROM...的方法將要保留的數據備份到另一個表中,將原來的表刪除,然後再 ALTER TABLE RENAME TO 命令將備份的表改爲原來表的名字。
這個方法由於不寫日誌文件,所以速度很快,但是原來的表所擁有的索引和約束都將不存在,需重新建立。另外這個方法只適用於ORACLE 8以後的版本,ORACLE 7.3中也可採用這個方法,但NOLOGGIN關鍵字要由UNRECOVERABLE代替。

用turncate table table1
因爲truncate 是DDL操作,不產生rollback,不寫日誌速度快一些,然後如果有自增的話,恢復到1開始
而delete會產生rollback,如果刪除大數據量的錶速度會很慢,同時會佔用很多的rollback segments,
同時還要記錄下G級別的日誌


1.選出您所需要保留的記錄到新的表
Select * into Table2 From Table1 Where Time>='2006-03-10'

2.然後直接Truncate table Table1。無論何種恢復模式都不會進行日誌記錄
Truncate table Table1

3.最後對Table2進行改名爲Table1
exec sp_rename 'Table2','Table1'

有兩種解決辦法:
1、你在建表時可以使用nologging選項,如:
    create   table   table_name
    (
      ...
    )
    nologging;
2、可以在insert時使用/*+   DIRECT   */   hint,如:
  insert   /*+   DIRECT   */   into   table_name   as   select   *   from   ....;

create table xxx as select * from original_table where preclude condition;
drop original_table;
rename xxx to original_table;

發佈了46 篇原創文章 · 獲贊 7 · 訪問量 29萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章