Oracle触发器以防止在某个条件下插入新行

时间:2018-06-08 18:11:55

标签: sql oracle plsql triggers

我发现很少有问题可以解决同一个问题但没有更好的解决方案。 我需要创建一个Oracle触发器来阻止条件上的新插入,但是要静默(不引发错误)。

Ex:我需要停止插入只有bar ='FOO'的行。 (我无法编辑表的约束,无法访问真正插入的程序等,因此触发器是唯一的选项)

到目前为止,解决方案证实这是不可能的。一个有希望的建议是创建一个中间表,当bar ='FOO'时插入键值,然后在插入完成后从原始表中删除那些记录,这是不正确的。 任何答案都将受到高度赞赏。

2 个答案:

答案 0 :(得分:0)

显然,在不引发异常的情况下,无法使用触发器来停止插入。

但是,如果您有权访问架构(并询问触发器可能没问题),您可以考虑用视图替换表而不是触发器。

作为当前表格的最小模拟。 myrole只是表格中授予的特权的一个代表:

CREATE ROLE myrole;

CREATE TABLE mytable (
  bar VARCHAR2(30)
);
GRANT ALL ON mytable TO myrole;

现在您重命名该表并确保没有人可以直接访问它,并将其替换为视图。此视图可以通过而不是触发器来保护:

REVOKE ALL ON mytable FROM myrole;
RENAME mytable TO myrealtable;

CREATE OR REPLACE VIEW mytable AS SELECT * FROM myrealtable;
GRANT ALL ON mytable TO myrole;

CREATE OR REPLACE TRIGGER myioftrigger 
  INSTEAD OF INSERT ON mytable
  FOR EACH ROW
BEGIN
  IF :new.bar = 'FOO' THEN
    NULL;
  ELSE 
    INSERT INTO myrealtable(bar) VALUES (:new.bar);
  END IF;
END;
/

所以,如果有人在假视图中插入一个普通行,那么数据会被插入真实表中:

INSERT INTO mytable(bar) VALUES('OK');
1 row inserted.

SELECT * FROM mytable;
OK

但如果有人插入魔法值“FOO”,那么触发器会默默地吞下它并且在真实表中没有任何变化:

INSERT INTO mytable(bar) VALUES('FOO');
1 row inserted.

SELECT * FROM mytable;
OK

警告:如果您还要保护表免受UPDATE的影响,则必须为更新添加第二个触发器。

答案 1 :(得分:0)

一种方法是隐藏行。从12c开始,这很容易:

create table demo
( id integer primary key
, bar varchar2(10) );

-- This adds a hidden column and registers the table for in-database archiving:
alter table demo row archival;

-- Set the hidden column to '1' when BAR='FOO', else '0':
create or replace trigger demo_hide_foo_trg
before insert or update on demo
for each row
begin
    if :new.bar = 'FOO' then
        :new.ora_archive_state := '1';
    else
        :new.ora_archive_state := '0';
    end if;
end demo_hide_foo_trg;
/

-- Enable in-database archiving for the session
-- (probably you could set this in a log-on trigger):
alter session set row archival visibility = active;

insert into demo (id, bar) values (1, 'ABC');
insert into demo (id, bar) values (2, 'FOO');
insert into demo (id, bar) values (3, 'XYZ');

commit;

select * from demo;

      ID BAR
-------- --------
       1 ABC
       3 XYZ

-- If you want to see all rows (e.g. to delete hidden rows):
alter session set row archival visibility = all;

在早期版本的Oracle中,您可以使用安全策略实现相同的目标。

另一种方法可能是添加一个' required'标志默认为' Y'并将其设置为“N' N' N'在bar = 'FOO'时触发,并且(假设您无法更改应用程序以使用视图等)有第二个触发器删除所有这些行(或者更好,将它们移动到存档表)。 / p>

create table demo
( id integer primary key
, bar varchar2(10) );

alter table demo add required_yn varchar2(1) default on null 'Y';

create or replace trigger demo_set_not_required_trg
before insert or update on demo
for each row
begin
    if :new.bar = 'FOO' then
        :new.required_yn := 'N';
    end if;
end demo_hide_foo_trg;
/

create or replace trigger demo_delete_not_required_trg
after insert or update on demo
begin
    delete demo where required_yn = 'N';
end demo_delete_not_required_trg;
/