1

我想用 ddl 触发器(在 drop 之前)创建一个备份表,并遇到了以下问题。

第一次 drop 发生时没关系: a_backup 表包含已删除表的数据。但是为什么在此之后我不能删除另一个表?

ORA-01031: 权限不足



create table b (x number);

-- 表 B 已创建。

create table a (x number);

-- 表 A 已创建。

create table a_backup as select * from a where 1 = 0;

-- 表 A_BACKUP 创建。

create or replace trigger a_backup_tr
    before drop
    on database
begin
    IF ora_dict_obj_name <> 'A' then
    null;
    ELSIF ora_dict_obj_name = 'A'
    and ora_dict_obj_owner = 'TRANEE' then
    insert into a_backup
    select * from a;
    ELSE null;
    end if;
end;
/

-- 触发 A_BACKUP_TR 编译


-- 1

drop table a;

-- 表 A 掉线。


-- 2

drop table b;

-- ORA-04045: 在重新编译/重新验证 TRANEE.A_BACKUP_TR 期间出错

-- ORA-01031: 权限不足

并且您不能在删除后删除任何表,除非您再次运行创建或替换触发器脚本。IF-THEN 部分有问题吗?当表 A 不存在时,IF 语句必须进入 NULL 吗?

4

1 回答 1

5

但是为什么我不能在此之后删除另一个表?

insert into a_backup select * from a; 

在触发器中,您明确地引用了表 A,但此时它并不存在。

您可以使用动态 SQL:

create or replace trigger a_backup_tr
    before drop
    on database
begin
    IF ora_dict_obj_name <> 'A' then
        null;
    ELSIF ora_dict_obj_name = 'A' and ora_dict_obj_owner = 'TRANEE' then
        EXECUTE IMMEDIATE 'insert into tranee.a_backup select * from tranee.a';
    ELSE null;
    end if;
end;
/

就我个人而言,我不喜欢为这种机制使用触发器的想法。SELECT *如果模式在未来发生漂移,也会盲目插入并且可能会失败。也许更好的方法是Flashback Drop(回收站)


编辑:

正如@wolφi 所提到的,为了减轻盲插入,您可以在触发器中创建表:

create or replace trigger a_backup_tr
    before drop
    on database
begin
    IF ora_dict_obj_name <> 'A' then
      null;
    ELSIF ora_dict_obj_name = 'A' and ora_dict_obj_owner = 'TRANEE' then
      --TODO: additional check if table already exists
      EXECUTE IMMEDIATE 'CREATE TABLE tranee.a_backup AS SELECT * FROM tranee.a';
    ELSE null;
    end if;
end;
/
于 2018-07-06T16:03:55.053 回答