0

我不知道这是否可能,但我想将一堆记录从临时表复制到普通表。问题是某些记录可能违反检查约束,因此我想插入所有可能的内容并在其他地方为无效记录生成错误日志。

如果我执行:

INSERT INTO normal_table
  SELECT ... FROM temp_table

如果任何记录违反任何约束,则不会插入任何内容。我可以做一个循环并手动插入,但我认为性能会更低。

Ps:如果可能的话,我想要一个适用于 Oracle 9 的解决方案

4

2 回答 2

3

在 Oracle 10gR2 中,您可以使用以下log errors子句

EXECUTE DBMS_ERRLOG.CREATE_ERROR_LOG('NORMAL_TABLE');
INSERT INTO normal_table
   SELECT ... FROM temp_table
   LOG ERRORS REJECT LIMIT UNLIMITED;

以最简单的形式。然后你可以看到你得到了什么错误:

SELECT ora_err_mesg$
FROM err$_normal_table;

更多关于这里CREATE_ERROR_LOG的步骤。


我认为这种方法适用于 9i,但没有可用于测试的实例,因此这实际上是在 11gR2
更新上运行的:在 9i 中测试和调整(以避免 PLS-00436):

declare
    type t_temp_table is table of temp_table%rowtype;
    l_temp_table t_temp_table;
    l_err_code err_table.err_code%type;
    l_err_msg err_table.err_msg%type;
    l_id err_table.id%type;

    cursor c is select * from temp_table;

    error_array exception;
    pragma exception_init(error_array, -24381);
begin
    open c;
    loop
        fetch c bulk collect into l_temp_table limit 100;
        exit when l_temp_table.count = 0;

        begin
            forall i in 1..l_temp_table.count save exceptions
                insert into normal_table
                values l_temp_table(i);
        exception
            when error_array then
                for j in 1..sql%bulk_exceptions.count loop
                    l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
                    l_err_code := sql%bulk_exceptions(j).error_code;
                    l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
                    insert into err_table(id, err_code, err_msg)
                    values (l_id, l_err_code, l_err_msg);
                end loop;
        end;
    end loop;
end;
/

使用您所有的真实列而不是id,我只是出于演示目的而这样做:

create table normal_table(id number primary key);
create table temp_table(id number);
create table err_table(id number, err_code number, err_msg varchar2(2000));

insert into temp_table values(42);
insert into temp_table values(42);

然后运行上面的匿名块...

select * from normal_table;

        ID
----------
        42

column err_msg format a50
select * from err_table;

        ID   ERR_CODE ERR_MSG                                          
---------- ---------- --------------------------------------------------
        42          1 ORA-00001: unique constraint (.) violated          

这在几个级别上不太令人满意 - 更多的编码,如果你有很多异常(因为这些异常的单独插入)会更慢,不显示违反了哪个约束(或任何其他错误细节),并且不会如果您回滚,请保留错误 - 尽管如果这是一个问题,您可以调用一个自治事务来记录它,我在这里对此表示怀疑。

如果您的数据量足够小,不想担心该limit子句,您可以稍微简化一下:

declare
    type t_temp_table is table of temp_table%rowtype;
    l_temp_table t_temp_table;
    l_err_code err_table.err_code%type;
    l_err_msg err_table.err_msg%type;
    l_id err_table.id%type;

    error_array exception;
    pragma exception_init(error_array, -24381);
begin
    select * bulk collect into l_temp_table from temp_table;

    forall i in 1..l_temp_table.count save exceptions
        insert into normal_table
        values l_temp_table(i);
exception
    when error_array then
        for j in 1..sql%bulk_exceptions.count loop
            l_id := l_temp_table(sql%bulk_exceptions(j).error_index).id;
            l_err_code := sql%bulk_exceptions(j).error_code;
            l_err_msg := sqlerrm(-1 * sql%bulk_exceptions(j).error_code);
            insert into err_table(id, err_code, err_msg)
            values (l_id, l_err_code, l_err_msg);
        end loop;
end;
/

9i 文档似乎不再在线,但这是在一个新功能文档中,并且很多人都写过它——这里也有人问过它。

于 2013-02-15T11:41:36.367 回答
0

如果您只对检查约束特别感兴趣,那么可以考虑的一种方法是从数据字典中读取目标检查约束的定义,并将它们作为谓词应用于使用动态 sql 从源表中提取数据的查询。

鉴于:

 create table t1 (
   col1 number check (col1 between 3 and 10))

你可以:

select  constraint_name,
        search_condition
from    user_constraints
where   constraint_type = 'C' and
        table_name = 'T1'

结果是:

"SYS_C00226681", "col1 介于 3 和 10 之间"

正如他们所说,从那里开始,这是“一个简单的编码问题”,并且该方法几乎适用于任何版本的 Oracle。最有效的方法可能是使用多表插入将行定向到预期的目标表或基于应用检查约束谓词的 CASE 语句的结果的错误记录表。

于 2013-02-15T14:43:17.767 回答