user_tab_columns何时更新?

时间:2016-11-04 14:00:16

标签: oracle plsql triggers ddl alter

在另一个question中,我试图创建一个hist表,它保存给定表的日志。在这个问题的答案中,我试图创造新的东西。

由于无法在表或视图上创建系统触发器,因此我创建了一个DDL触发器:

create or replace trigger ident_hist_trig after alter on schema
declare
    v_table varchar2(30);
begin
    select upper(ora_dict_obj_name) into v_table from dual;
    if (v_table = 'Z_IDENT') then
        prc_create_hist_tabel('z_ident_hist', 'z_ident');
    elsif (v_table = 'D_IDENT') then
        prc_create_hist_tabel('d_ident_hist', 'd_ident');
    elsif (v_table = 'X_IDENT') then
        prc_create_hist_tabel('x_ident_hist', 'x_ident');
    else
        null;
    end if;
end;
/

程序prc_create_hist_tabel看起来像这样:

create or replace procedure prc_create_hist_tabel(p_naam_hist_tabel in varchar2, p_naam_tabel in varchar2) is
    cursor c is 
        select 'alter table ' || p_naam_hist_tabel || ' add ' || column_name || ' ' || data_type || case when data_type = 'DATE' then null else '(' || data_length || ')' end lijn 
        from user_tab_columns 
        where TABLE_NAME = upper(p_naam_tabel) 
        and column_name not in (select column_name from user_tab_columns where table_name = upper(p_naam_hist_tabel));
    v_dummy number(1);
    cursor trig is
        select column_name || ',' kolom, ':old.' || column_name || ',' old
        from user_tab_columns
        where table_name = upper(p_naam_tabel);
    v_trigger_sql varchar2(32767);
begin
    begin
        select 1 into v_dummy
        from user_tab_columns
        where TABLE_NAME = upper(p_naam_hist_tabel)
        group by 1;
    exception when no_data_found then
        execute immediate 'create table ' || p_naam_hist_tabel || ' (wijziger varchar2(60) default user, wijzigdatum date default sysdate, constraint pk_' || p_naam_hist_tabel || ' primary key (wijziger, wijzigdatum))';
    end;
    dbms_output.put_line('BBB');
    for i in c
    loop
        begin
            dbms_output.put_line(i.lijn);
            execute immediate i.lijn;
        exception when others then
            dbms_output.put_line(i.lijn);
        end;
    end loop;

    v_trigger_sql := 'create or replace trigger ' || p_naam_tabel || '_hist_trig after update on ' || p_naam_tabel || ' for each row begin insert into ' || p_naam_hist_tabel || ' (';
    for v_lijn in trig
    loop
        v_trigger_sql := v_trigger_sql || v_lijn.kolom;
    end loop;
    v_trigger_sql := substr(v_trigger_sql, 1, length(v_trigger_sql) - 1);
    v_trigger_sql := v_trigger_sql || ') values (';
    for v_lijn in trig
    loop
        v_trigger_sql := v_trigger_sql || v_lijn.old;
    end loop;
    v_trigger_sql := substr(v_trigger_sql, 1, length(v_trigger_sql) - 1);
    v_trigger_sql := v_trigger_sql || '); end;';

    execute immediate v_trigger_sql;
end;
/

简而言之,该功能的作用是维护历史表。如果它不存在,它将创建一个,如果存在,它将向其添加新列。该过程还会创建一个新触发器,在更新后将旧值写入历史记录表。

但是当我改变其中一个表x_ident,z_ident或d_ident时,光标c将不返回任何内容(当我循环时,我可以通过打印来检查它)。虽然在执行之后选择后我改变了我的表格,但我确实得到了结果。

我从改变表d_ident得到的结果是:

BBB

d_ident: Table altered.

但我想它应该是另一种方式,我认为程序prc_create_hist_tabel是在alter table实际关闭之前执行的,我想我应该得到这样的东西:

d_ident: Table altered.

BBB

任何帮助都会被贬低。我尝试在user_tab_columns上的insert上创建一个触发器,但这给了我ORA-25001:无法在视图上创建此触发器类型。

我也尝试过睡眠命令,但这也没有用。

1 个答案:

答案 0 :(得分:1)

这不起作用。即使您能够在触发器中获取正在添加到表中的列,但如果您尝试在触发器中实际执行DDL,则会在触发器中出现DDL不允许的错误。

我希望正确的方法是将prc_create_hist_tabel作为促销脚本的一部分进行调用。合理的系统不会无缘无故地向表中添加列。 DDL是源代码管理中存在的促销的一部分,并在测试后部署。如果您的促销脚本无法修改历史记录表,您将在测试期间发现您错过了一个步骤,并且更改将永远不会进入生产阶段。自动进行更改意味着它们不在变更控制中,这使得从变更控制进行构建变得更加困难。

如果您决定自动执行此操作,则您的触发器需要提交作业,实际使用调用该过程的dbms_job而不是较新的dbms_scheduler。该事务将在事务之后运行,DDL触发器是已提交的一部分。此时,该列将在dba_tab_columns中可见。你的工作可以自由地做DDL。