在另一个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:无法在视图上创建此触发器类型。
我也尝试过睡眠命令,但这也没有用。
答案 0 :(得分:1)
这不起作用。即使您能够在触发器中获取正在添加到表中的列,但如果您尝试在触发器中实际执行DDL,则会在触发器中出现DDL不允许的错误。
我希望正确的方法是将prc_create_hist_tabel
作为促销脚本的一部分进行调用。合理的系统不会无缘无故地向表中添加列。 DDL是源代码管理中存在的促销的一部分,并在测试后部署。如果您的促销脚本无法修改历史记录表,您将在测试期间发现您错过了一个步骤,并且更改将永远不会进入生产阶段。自动进行更改意味着它们不在变更控制中,这使得从变更控制进行构建变得更加困难。
如果您决定自动执行此操作,则您的触发器需要提交作业,实际使用调用该过程的dbms_job
而不是较新的dbms_scheduler
。该事务将在事务之后运行,DDL触发器是已提交的一部分。此时,该列将在dba_tab_columns
中可见。你的工作可以自由地做DDL。