Oracle DML容错处理(2)
关于DML Error Logging效率的问题,摘自网上一篇文章,作为单独一篇说明,原文如下:
DML Error Logging in Oracle 10g Database Release 2
In some situations the most obvious solution to a problem is a DML statement (INSERT ... SELECT, UPDATE, DELETE), but you may choose to avoid DML because of the way it reacts to exceptions. By default, when a DML statement fails the whole statement is rolled back, regardless of how many rows were processed successfully before the error was detected. In the past, the only way around this problem was to process each row individually, preferably with a bulk operation using FORALL and the SAVE EXCEPTIONS clause. In Oracle 10g Database Release 2, the DML error logging feature has been introduced to solve this problem. Adding the appropriate LOG ERRORS clause on to most INSERT, UPDATE, MERGE and DELETE statements enables the operations to complete, regardless of errors. This article presents an overview of the DML error logging functionality, with examples of each type of DML statement.
- Syntax
- Restrictions
- Sample Schema
- Insert
- Update
- Merge
- Delete
- Performance
Syntax
The syntax for the error logging clause is the same for INSERT, UPDATE, MERGE and DELETE statements.
LOG ERRORS [INTO [schema.]table] [('simple_expression')] [REJECT LIMIT integer|UNLIMITED]
The optional INTO clause allows you to specify the name of the error logging table. If you omit this clause, the the first 25 characters of the base table name are used along with the "ERR$_" prefix.
The simple_expression is used to specify a tag that makes the errors easier to identify. This might be a string or any function whose result is converted to a string.
The REJECT LIMIT is used to specify the maximum number of errors before the statement fails. The default value is 0 and the maximum values is the keyword UNLIMITED. For parallel DML operations, the reject limit is applied to each parallel server.
Restrictions
The DML error logging functionality is not invoked when:
- Deferred constraints are violated.
- Direct-path INSERT or MERGE operations raise unique constraint or index violations.
- UPDATE or MERGE operations raise a unique constraint or index violation.
In addition, the tracking of errors in LONG, LOB and object types is not supported, although a table containing these columns can be the target of error logging.
Sample Schema
This following code creates and populates the tables necessary to run the example code in this article.
-- Create and populate a source table.
CREATE TABLE source (
id NUMBER(10) NOT NULL,
code VARCHAR2(10),
description VARCHAR2(50),
CONSTRAINT source_pk PRIMARY KEY (id)
);
DECLARE
TYPE t_tab IS TABLE OF source%ROWTYPE;
l_tab t_tab := t_tab();
BEGIN
FOR i IN 1 .. 100000 LOOP
l_tab.extend;
l_tab(l_tab.last).id := i;
l_tab(l_tab.last).code := TO_CHAR(i);
l_tab(l_tab.last).description := 'Description for ' || TO_CHAR(i);
END LOOP;
-- For a possible error condition.
l_tab(1000).code := NULL;
l_tab(10000).code := NULL;
FORALL i IN l_tab.first .. l_tab.last
INSERT INTO source VALUES l_tab(i);
COMMIT;
END;
/
EXEC DBMS_STATS.gather_table_stats(USER, 'source', cascade => TRUE);
-- Create a destination table.
CREATE TABLE dest (
id NUMBER(10) NOT NULL,
code VARCHAR2(10) NOT NULL,
description VARCHAR2(50),
CONSTRAINT dest_pk PRIMARY KEY (id)
);
-- Create a dependant of the destination table.
CREATE TABLE dest_child (
id NUMBER,
dest_id NUMBER,
CONSTRAINT child_pk PRIMARY KEY (id),
CONSTRAINT dest_child_dest_fk FOREIGN KEY (dest_id) REFERENCES dest(id)
);
Notice that the CODE column is optional in the SOURCE table and mandatory in the DEST table.
Once the basic tables are in place we can create a table to hold the DML error logs for the DEST. A log table must be created for every base table that requires the DML error logging functionality. This can be done manually or with the CREATE_ERROR_LOG procedure in the DBMS_ERRLOG package, as shown below.
-- Create the error logging table.
BEGIN
DBMS_ERRLOG.create_error_log (dml_table_name => 'dest');
END;
/
PL/SQL procedure successfully completed.
SQL>
The owner, name and tablespace of the log table can be specified, but by default it is created in the current schema, in the default tablespace with a name that matches the first 25 characters of the base table with the "ERR$_" prefix.
SELECT owner, table_name, tablespace_name
FROM all_tables
WHERE owner = 'TEST';
OWNER TABLE_NAME TABLESPACE_NAME
------------------------------ ------------------------------ ------------------------------
TEST DEST USERS
TEST DEST_CHILD USERS
TEST ERR$_DEST USERS
TEST SOURCE USERS
4 rows selected.
SQL>
The structure of the log table includes maximum length and datatype independent versions of all available columns from the base table, as seen below.
SQL> DESC err$_dest
Name Null? Type
--------------------------------- -------- --------------
ORA_ERR_NUMBER$ NUMBER
ORA_ERR_MESG$ VARCHAR2(2000)
ORA_ERR_ROWID$ ROWID
ORA_ERR_OPTYP$ VARCHAR2(2)
ORA_ERR_TAG$ VARCHAR2(2000)
ID VARCHAR2(4000)
CODE VARCHAR2(4000)
DESCRIPTION VARCHAR2(4000)
SQL>
Insert
When we built the sample schema we noted that the CODE column is optional in the SOURCE table, but mandatory in th DEST table. When we populated the SOURCE table we set the code to NULL for two of the rows. If we try to copy the data from the SOURCE table to the DEST table we get the following result.
INSERT INTO dest
SELECT *
FROM source;
SELECT *
*
ERROR at line 2:
ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
SQL>
The failure causes the whole insert to roll back, regardless of how many rows were inserted successfully. Adding the DML error logging clause allows us to complete the insert of the valid rows.
INSERT INTO dest
SELECT *
FROM source
LOG ERRORS INTO err$_dest ('INSERT') REJECT LIMIT UNLIMITED;
99998 rows created.
SQL>
The rows that failed during the insert are stored in the ERR$_DEST table, along with the reason for the failure.
COLUMN ora_err_mesg$ FORMAT A70
SELECT ora_err_number$, ora_err_mesg$
FROM err$_dest
WHERE ora_err_tag$ = 'INSERT';
ORA_ERR_NUMBER$ ORA_ERR_MESG$
--------------- ---------------------------------------------------------
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
2 rows selected.
SQL>
Update
The following code attempts to update the CODE column for 10 rows, setting it to itself for 8 rows and to the value NULL for 2 rows.
UPDATE dest
SET code = DECODE(id, 9, NULL, 10, NULL, code)
WHERE id BETWEEN 1 AND 10;
*
ERROR at line 2:
ORA-01407: cannot update ("TEST"."DEST"."CODE") to NULL
SQL>
As expected, the statement fails because the CODE column is mandatory. Adding the DML error logging clause allows us to complete the update of the valid rows.
UPDATE dest
SET code = DECODE(id, 9, NULL, 10, NULL, code)
WHERE id BETWEEN 1 AND 10
LOG ERRORS INTO err$_dest ('UPDATE') REJECT LIMIT UNLIMITED;
8 rows updated.
SQL>
The rows that failed during the update are stored in the ERR$_DEST table, along with the reason for the failure.
COLUMN ora_err_mesg$ FORMAT A70
SELECT ora_err_number$, ora_err_mesg$
FROM err$_dest
WHERE ora_err_tag$ = 'UPDATE';
ORA_ERR_NUMBER$ ORA_ERR_MESG$
--------------- ---------------------------------------------------------
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
2 rows selected.
SQL>
Merge
The following code deletes some of the rows from the DEST table, then attempts to merge the data from the SOURCE table into the DEST table.
DELETE FROM dest
WHERE id > 50000;
MERGE INTO dest a
USING source b
ON (a.id = b.id)
WHEN MATCHED THEN
UPDATE SET a.code = b.code,
a.description = b.description
WHEN NOT MATCHED THEN
INSERT (id, code, description)
VALUES (b.id, b.code, b.description);
*
ERROR at line 9:
ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
SQL>
As expected, the merge operation fails and rolls back. Adding the DML error logging clause allows the merge operation to complete.
MERGE INTO dest a
USING source b
ON (a.id = b.id)
WHEN MATCHED THEN
UPDATE SET a.code = b.code,
a.description = b.description
WHEN NOT MATCHED THEN
INSERT (id, code, description)
VALUES (b.id, b.code, b.description)
LOG ERRORS INTO err$_dest ('MERGE') REJECT LIMIT UNLIMITED;
99998 rows merged.
SQL>
The rows that failed during the update are stored in the ERR$_DEST table, along with the reason for the failure.
COLUMN ora_err_mesg$ FORMAT A70
SELECT ora_err_number$, ora_err_mesg$
FROM err$_dest
WHERE ora_err_tag$ = 'MERGE';
ORA_ERR_NUMBER$ ORA_ERR_MESG$
--------------- ---------------------------------------------------------
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
1400 ORA-01400: cannot insert NULL into ("TEST"."DEST"."CODE")
2 rows selected.
SQL>
Delete
The DEST_CHILD table has a foreign key to the DEST table, so if we add some data to it would would expect an error if we tried to delete the parent rows from the DEST table.
INSERT INTO dest_child (id, dest_id) VALUES (1, 100);
INSERT INTO dest_child (id, dest_id) VALUES (2, 101);
With the child data in place we ca attempt to delete th data from the DEST table.
DELETE FROM dest;
*
ERROR at line 1:
ORA-02292: integrity constraint (TEST.DEST_CHILD_DEST_FK) violated - child record found
SQL>
As expected, the delete operation fails. Adding the DML error logging clause allows the delete operation to complete.
DELETE FROM dest
LOG ERRORS INTO err$_dest ('DELETE') REJECT LIMIT UNLIMITED;
99996 rows deleted.
SQL>
The rows that failed during the delete operation are stored in the ERR$_DEST table, along with the reason for the failure.
COLUMN ora_err_mesg$ FORMAT A69
SELECT ora_err_number$, ora_err_mesg$
FROM err$_dest
WHERE ora_err_tag$ = 'DELETE';
ORA_ERR_NUMBER$ ORA_ERR_MESG$
--------------- ---------------------------------------------------------------------
2292 ORA-02292: integrity constraint (TEST.DEST_CHILD_DEST_FK) violated -
child record found
2292 ORA-02292: integrity constraint (TEST.DEST_CHILD_DEST_FK) violated -
child record found
2 rows selected.
SQL>
Performance
The performance of DML error logging depends on the way it is being used and what version of the database you use it against. Prior to Oracle 12c, you will probably only use DML error logging during direct path loads, since conventional path loads become very slow when using it. The following example displays this, but before we start we will need to remove the extra dependency table.
DROP TABLE dest_child PURGE;
Truncate the destination table and run a conventional path load using DML error logging, using SQL*Plus timing to measure the elapsed time.
SET TIMING ON
TRUNCATE TABLE dest;
INSERT INTO dest
SELECT *
FROM source
LOG ERRORS INTO err$_dest ('INSERT NO-APPEND') REJECT LIMIT UNLIMITED;
99998 rows created.
Elapsed: 00:00:08.61
SQL>
Next, repeat the test using a direct path load this time.
TRUNCATE TABLE dest;
INSERT /*+ APPEND */ INTO dest
SELECT *
FROM source
LOG ERRORS INTO err$_dest ('INSERT APPEND') REJECT LIMIT UNLIMITED;
99998 rows created.
Elapsed: 00:00:00.38
SQL>
Finally, perform the same load using FORALL ... SAVE EXCEPTIONS method.
TRUNCATE TABLE dest;
DECLARE
TYPE t_tab IS TABLE OF dest%ROWTYPE;
l_tab t_tab;
l_start PLS_INTEGER;
CURSOR c_source IS
SELECT * FROM source;
ex_dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
BEGIN
OPEN c_source;
LOOP
FETCH c_source
BULK COLLECT INTO l_tab LIMIT 1000;
EXIT WHEN l_tab.count = 0;
BEGIN
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO dest VALUES l_tab(i);
EXCEPTION
WHEN ex_dml_errors THEN
NULL;
END;
END LOOP;
CLOSE c_source;
END;
/
PL/SQL procedure successfully completed.
Elapsed: 00:00:01.01
SQL>
From this we can see that DML error logging is very fast for direct path loads, but does not perform well for conventional path loads. In fact, it performs significantly worse than the FORALL ... SAVE EXCEPTIONS method.
The relative performance of these methods depends on the database version. The following table shows the results of the previous tests against a number of database versions. They are run on different servers, so don't compare version-to-version. Look at the comparison between the methods within a version.
10.2.0.4 11.2.0.3 11.2.0.4 12.1.0.1
======== ======== ======== ========
DML Error Logging : 07.62 08.61 04.82 00.94
DML Error Logging (APPEND) : 00.86 00.38 00.85 01.07
FORALL ... SAVE EXCEPTIONS : 01.15 01.01 00.94 01.37
Link:
https://oracle-base.com/articles/10g/dml-error-logging-10gr2
同样的方式不同数据库版本性能可能不同,同版本数据库下,使用直接路径加载DML error logging速度优于forall,使用传统路径加载DML error logging速度明显劣于forall。
Oracle DML容错处理(2)的更多相关文章
- Oracle DML容错处理(1)
Oracle dml操作过程中可能出现键重复或者数据类型不一致等问题,一般进行数据处理时候需要对这些可能出现的错误提前考虑,避免更新失败.Oralce给出了一些其他解决方案,以在不同场景下使用. 1. ...
- oracle DML语句
DML语句 1. 插入数据 创建一个新表 create table new_cust as select * from customers --使用insert语句添加行 /* 确定要插入的行所在的 ...
- Oracle(DML)
数据操作语言: insert update delete 事务控制语言: commit rollback savepoint 1.insert语句 两种格式: 直接插入 子查询插入 1. 直接插入基本 ...
- oracle DML错误日志(笔记)
DML错误日志是oracle10gR2引入的一个类似于SQL*Loader的错误日志功能.它的基本原理是把任何可能导致语句失败的记录转移,放到一张错误日志表中. 具体使用如下: 1.使用DBMS_ER ...
- oracle DML(数据管理语言)sql 基本语句
- oracle DML语句 事务的定义与特点
1.insert into (插入数据) insert in to 表名(列表1,列表2) values(要插入的数据1,数据2); or insert into 表名 values(数据 ...
- 转 如何观察 undo Oracle DML语句回滚开销估算
https://searchdatabase.techtarget.com.cn/7-20392/ --use_urec 详细解读: select USED_UREC from v$transacti ...
- Oracle ddl 和 dml 操作
ddl 操作 窗口设置用户权限的方法 Oracle的数据类型 按住Ctrl点击表名 ,可以鼠标操作 插入的数据需要满足创建表的检查 主表clazz删除数据从表设置级联也会一同删除 有约束也 ...
- ORACLE 11G R2 RAC classical install OGG12.1(LINUX) 经典抽取模式单项同步配置OGG12.1
博文结构图如下: 一.环境描述以及注意事项 1.1 环境简介 IP 系统 Oracle版本 OGG版本 源端 172.16.10.16/36 RHEL6.5 oracle11204 12.1 目标端 ...
随机推荐
- phantomjs 中如何使用xpath
function getNodeInfo(inputcsvPath) { var htmlnodeInfo = page.evaluate(function () { //_Ltg var XPATH ...
- java.sql.SQLException: ORA-28040: 没有匹配的验证协议(12c或者12c rac)
1.plsql可以连接,java程序不能连接,报如下错误: 一直以来用的都是服务器上的Oracle数据库,今天改成连接本地Oracle 12c数据库是出问题了.hibernate连接Oracle12c ...
- Centos6.5使用yum安装mysql——快速上手必备
第1步.yum安装mysql [root@stonex ~]# yum -y install mysql-server 安装结果: Installed: mysql-server.x86_6 ...
- Ajax jsonp 跨域请求实例
跨域请求 JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求:它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题. $. ...
- ios如何实现静音模式下声音仍然可以外放
AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory:AVAudioSes ...
- jquery submit ie6下失效的原因分析及解决方法
ie6中, $('a.btn').click(function(){ form.submit(); }) 点击失效: 分析: 微软低版本浏览器会先执行link标签的自身事件也就是href事件,这样就中 ...
- js十大排序算法详解
十大经典算法导图 图片名词解释:n: 数据规模k:“桶”的个数In-place: 占用常数内存,不占用额外内存Out-place: 占用额外内存 1.冒泡排序 1.1 原始人冒泡排序 functi ...
- LeetCode(59):螺旋矩阵 II
Medium! 题目描述: 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, ...
- java List.subList方法中的超级大陷阱
ArrayList 中 subList 的基本用法: subList(fromIndex:int,toIndex:int):List<E> 返回从fromIndex到toindex-1 的 ...
- DDD领域模型数据访问之对象(十一)
在工程DDD.Domain中文件夹ModelPermission新建类:BAS_Object public partial class BAS_Obejct:AggreateRoot { //仓储接口 ...