當一個DML運行的時候,如果遇到了錯誤,則這條語句會整個回滾,就好像沒有執行過。不過對於一個大的DML而言,如果個別數據錯誤而導致整個語句的回滾,會浪費很多的資源和運行時間,從10g開始Oracle支持記錄DML語句的錯誤,而允許語句自動繼續執行。下面介紹一下DML記錄語句的用法。
看一個插入語句的簡單例子:
SQL> CREATE TABLE T1 AS SELECT ROWNUM A,ROWNUM B FROM DBA_SEGMENTS WHERE ROWNUM <=10;
Table created
SQL> CREATE TABLE T2 AS SELECT ROWNUM A,ROWNUM B FROM DBA_SEGMENTS WHERE ROWNUM <=20;
Table created
SQL> ALTER TABLE T1 ADD CONSTRAINT PK_T1_A PRIMARY KEY(A);
Table altered
SQL> INSERT INTO T1 SELECT * FROM T2;
INSERT INTO T1 SELECT * FROM T2
ORA-00001: 違反唯一約束條件 (NREI.PK_T1_A)
可以看到,由於插入的數據違反了唯一性約束,導致了Oracle報錯。
下面創建記錄DML錯誤信息的記錄表,通過DBMS_ERRLOG包來進行創建,而這個包目前只包括這一個過程:
procedure create_error_log(dml_table_name varchar2,
err_log_table_name varchar2 default NULL,
err_log_table_owner varchar2 default NULL,
err_log_table_space varchar2 default NULL,
skip_unsupported boolean default FALSE);
利用CREATE_ERROR_LOG來創建T1表的DML錯誤記錄表:
SQL> EXEC DBMS_ERRLOG.CREATE_ERROR_LOG('T1','ERR_T1','NREI');
PL/SQL procedure successfully completed
可以看到Oracle創建的錯誤記錄表包括錯誤號碼ORA_ERR_NUMBER$,錯誤信息ORA_ERR_MESG$,記錄的ROWID信息ORA_ERR_ROWID$,錯誤操作類型ORA_ERR_OPTYP$,錯誤標簽ORA_ERR_TAG$,以及表中對應的列。
下面利用包含LOG ERROR語句的INSERT語句再次插入數據:
SQL> INSERT INTO T1 SELECT * FROM T2 LOG ERRORS INTO ERR_T1('ERR_T1')REJECT LIMIT UNLIMITED;
10 rows inserted
可以看到,插入成功執行,但是插入記錄為10條。從對應的錯誤信息表中已經包含了插入的信息。而且從錯誤信息表中還可以看到對應的錯誤號和詳細錯誤信息,ORA_ERR_OPTYP$為錯誤操作類型,I表示為Insert
關於LOG ERRORS的語法,INTO語句后面跟隨的就是指定的錯誤記錄表的表名。
在INTO語句后面,可以跟隨一個表達式('ERR_T1')即是ORA_ERR_TAG$中存儲的信息,用來設置本次語句執行的錯誤在錯誤記錄表中對應的TAG。有了這個語句,就可以很輕易的在錯誤記錄表中找到某次操作所對應的所有的錯誤,這對於錯誤記錄表中包含了大量數據,且本次語句產生了多條錯誤信息的情況十分有幫助。只要這個表達式的值可以轉化為字符串類型就可以。
而REJECT LIMIT則限制語句出錯的數量。
SQL> INSERT INTO T1 SELECT * FROM T2 LOG ERRORS INTO ERR_T1('ERR_T1')REJECT LIMIT 1;
INSERT INTO T1 SELECT * FROM T2 LOG ERRORS INTO ERR_T1('ERR_T1')REJECT LIMIT 1
ORA-00001: 違反唯一約束條件 (NREI.PK_T1_A)
可以看到,當設置的REJECT LIMIT的值小於出錯記錄數時,語句會報錯,這時LOG ERRORS語句沒有起到應有的作用,插入語句仍然以報錯結束。而如果將REJECT LIMIT的限制設置大於等於出錯的記錄數,則插入語句就會執行成功。而所有出錯的信息都會存儲到LOG ERROR對應的表中。
只要指定了LOG ERRORS語句,不管最終插入語句十分成功的執行完成,在錯誤記錄表中都會記錄語句執行過程中遇到的錯誤。比如第一個插入由於出錯數目超過REJECT LIMIT的限制,這時在記錄表中會存在REJECT LIMIT + 1條記錄數,因此這條記錄錯誤導致了整個SQL語句的報錯。
如果不管碰到多少錯誤,都希望語句能繼續執行,則可以設置REJECT LIMIT為UNLIMITED
需要注意的是, 即是做了回滾操作, ERR_T1 表中的記錄並不會減少,因為 Oracle 是利用自治事務的方式插入錯誤記錄表的。