Oracle PL/SQL - 如何在异常子句的单独表中捕获坏数据?

Posted

技术标签:

【中文标题】Oracle PL/SQL - 如何在异常子句的单独表中捕获坏数据?【英文标题】:Oracle PL/SQL - How to catch the bad data in a separate table in the exception clause? 【发布时间】:2017-03-02 04:25:45 【问题描述】:

下面是代码

declare
   cursor c_data
   is
      select * from test_product_u;
begin
   for i in c_data
   loop   
      insert into test_product_u_final
                (PRODUCT_NO, CREATED_DATE, DATE_FORMAT) 
         values (i. PRODUCT_NO, i.CREATED_DATE,i.DATE_FORMAT);
   end loop;
exception when others then
---->'I want to catch the bad data here ? What are the options apart from the sqlerror message I want the data itself possibly in a VARCHAR column'<----
end;

    我想在异常子句中捕获坏数据?除了获取 sqlerror 消息之外还有哪些选项我希望数据本身可能在 VARCHAR 列中?有可能吗。

    DW 加载捕获无效数据时通常会发生什么?

【问题讨论】:

我们在谈论多少数据? 本例仅假设 10 条记录,但对于 DW 每晚加载将超过 500,000 条记录。 那么 10 行的方法与 500,000 行的方法大不相同 :) 你想捕捉什么样的错误? 任何不符合表数据定义的错误,如NOT NULL或DATA TYPE MISMATCH。无论错误如何,我都希望将传入数据的快照记录到异常表中。 【参考方案1】:

根据代码示例,您只需将 BEGIN END 放入 LOOP 即可成功将错误记录在单独的错误表中。下面我举例说明了。希望这会有所帮助。

DECLARE
  CURSOR c_data
  IS
    SELECT * FROM test_product_u;
BEGIN
  FOR i IN c_data
  LOOP
    BEGIN
      INSERT
      INTO test_product_u_final
        (
          PRODUCT_NO,
          CREATED_DATE,
          DATE_FORMAT
        )
        VALUES
        (
          i. PRODUCT_NO,
          i.CREATED_DATE,
          i.DATE_FORMAT
        );
    EXCEPTION
    WHEN OTHERS THEN
      INSERT
      INTO bad_data_tab VALUES
        (
          i.product_no
          ||';'
          ||i.created_date
          ||';'
          ||i.date_format
        );
    END;
  END LOOP;
EXCEPTION
WHEN OTHERS THEN
  RAISE_APPLICATION_ERROR(-20009,SQLERRM,TRUE);
END;

【讨论】:

所以如果有一个坏行,上面的代码将所有行插入到坏表中。您可以拆分循环以仅将坏行推送到实际目的地吗? 它完全按照您的要求进行操作。将好的行插入目标并将坏记录加载到 bad_table。 为什么按照我的回答,当您可以使用基于集合的 SQL 更快(数量级)更快地执行此操作时(或我的团队所说的“慢慢来”)。 . 是的,你是对的。我的目的是让我的答案更接近 OP 问题,以便只需进行最少的更改。【参考方案2】:

从根本上说,采用的方法是尽可能多地使用基于集合的处理,这将(字面上)比逐行方法快 100 倍。大多数数据库验证都可以使用基于集合的 SQL 的强大功能来完成。此外,如果你有硬件(CPU 和 IO 带宽),很容易并行化以获得更高的性能。 大多数 DW 加载采用“瀑布式”方法,从一个表加载到另一个表,直到数据最终干净为止。 以你的一个数据检查为例,NOT NULL,它可以写成这样,使用多表插入。

insert /*+ APPEND */ all
when ( col is null ) 
then
into table product_error_1 ( PRODUCT_NO, CREATED_DATE, DATE_FORMAT) values (PRODUCT_NO, CREATED_DATE, DATE_FORMAT )
else
into table product_stage_2 ( PRODUCT_NO, CREATED_DATE, DATE_FORMAT ) values ( PRODUCT_NO, CREATED_DATE, DATE_FORMAT )
select * from test_product_u
/

【讨论】:

【参考方案3】:
create table test_product_u
(product_no varchar2(1024),
created_date varchar2(1024),
date_format varchar2(1024) default 'yyyy-mon-dd');

create table test_product_u_final
(product_no varchar2(1024),
created_date date not null,
date_format varchar2(1024) default 'yyyy-mon-dd');

insert into test_product_u(product_no, created_date)
values('A', '2017-mar-02');
insert into test_product_u(product_no, created_date)
values('B', null);
insert into test_product_u(product_no, created_date)
values('C', '28-february-2017');

commit;

方法一:

begin
  dbms_errlog.create_error_log('TEST_PRODUCT_U_FINAL', 'TEST_PRODUCT_U_FINAL$ERR') ;
end;

declare
  cursor c_data is
    select * from test_product_u;
begin
  for i in c_data loop
    insert into test_product_u_final
      (product_no, created_date, date_format)
    values
      (i. product_no, i.created_date, i.date_format) log errors into test_product_u_final$err
      ('INSERT') reject limit unlimited;
  end loop;
end;

方法二:

declare
  cursor c_data is
    select * from test_product_u;
begin
  for i in c_data loop
    begin
      insert into test_product_u_final
        (product_no, created_date, date_format)
      values
        (i. product_no, i.created_date, i.date_format);
    exception
      when others then
        dbms_output.put_line('========== Exception (Start) ===============');
        dbms_output.put_line('Product: ' || i.product_no);
        dbms_output.put_line('Created Date: ' || i.created_date);
        dbms_output.put_line('Date Format: ' || i.date_format);
        dbms_output.put_line('Error Code: ' || sqlcode);
        dbms_output.put_line('Error Text: ' || sqlerrm);
        dbms_output.put_line('Error Trace: ' || dbms_utility.format_error_backtrace);
        dbms_output.put_line('========== Exception (End) ===============');
    end;
  end loop;
end;

并输出:

  ========== Exception (Start) ===============
  Product: A
  Created Date: 2017-mar-02
  Date Format: yyyy-mon-dd
  Error Code: -1861
  Error Text: ORA-01861: literal does not match format string
  Error Trace: ORA-06512: at line 7

  ========== Exception (End) ===============
  ========== Exception (Start) ===============
  Product: B
  Created Date: 
  Date Format: yyyy-mon-dd
  Error Code: -1400
  Error Text: ORA-01400: cannot insert NULL into ("FCI"."TEST_PRODUCT_U_FINAL"."CREATED_DATE")
  Error Trace: ORA-06512: at line 7

  ========== Exception (End) ===============

【讨论】:

以上是关于Oracle PL/SQL - 如何在异常子句的单独表中捕获坏数据?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle的PL_SQL的异常处理

如何在 select 的 where 子句中替换 oracle pl/sql 变量

oracle 形式:pl/sql 中 where 子句中的 max 子句

如何在 PL/SQL 的 LIKE 子句中使用变量

SQL 查询的 SELECT 子句中 Oracle PL/SQL 语句的延迟评估

Oracle PL/SQL:在 SAMPLE 子句中使用变量时出现语法错误