user_tab_columns 什么时候更新?

When does user_tab_columns get updated?

在另一个 中,我尝试创建一个 hist table,它保留来自给定 table 的日志。有了那个问题的答案,我试图创造一些新的东西。

由于无法在 table 或视图上创建系统触发器,因此我创建了一个 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;
/

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

但是当我更改 table 中的一个 x_ident、z_ident 或 d_ident 时,光标 c 将 return 什么都没有(我可以检查当我遍历它时打印出来的那个)。尽管在执行 select after 时我更改了我的 table,但我确实得到了结果。

我改变 table d_ident 得到的结果是:

BBB

d_ident: Table altered.

但我想应该是相反的,我认为程序 prc_create_hist_tabel 是在 alter table 实际关闭之前执行的,我想我应该得到这样的结果:

d_ident: Table altered.

BBB

如有任何帮助,我们将不胜感激。我试图在 user_tab_columns 上的插入时创建触发器,但这给了我 ORA-25001:无法在视图上创建此触发器类型。

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

这行不通。即使您能够在触发器中获取要添加到 table 的列,如果您尝试在触发器中实际执行 DDL,您也会收到错误消息,指出触发器中不允许 DDL .

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

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