【问题标题】:Insert all records bar those in a banned list插入禁止列表中的所有记录
【发布时间】:2013-02-20 23:22:05
【问题描述】:

我在 table1 上插入触发器之前。如果不允许某些数据 (ID),则会引发应用程序错误。

但是,例如,当我使用insert into table1 select id from table2 where id in (1,2,3) 时,如果只允许 ID '3',其他 ID(1 和 2)也不会插入。

我该如何克服这个问题?触发代码类似:

CREATE OR REPLACE TRIGGER t1_before_insert BEFORE INSERT 
ON table1 
FOR EACH ROW 
DECLARE 
xx number(20); 
BEGIN 
select id into xx from blocked_id where id=:new.id; 
if :new.id=xx then raise_application_error(-20001, '--'); 
end if; 
END;

【问题讨论】:

    标签: oracle triggers insert


    【解决方案1】:

    我真的不喜欢以这种方式使用触发器和错误——数据完整性并不是触发器的真正用途。在我看来,这似乎是应用程序的一部分,应该包含在应用程序代码中,也许是在充当 API 插入表的过程中。

    【讨论】:

      【解决方案2】:

      好的,两点。首先,您的 SELECT INTO ... 冒着NO_DATA_FOUND 异常的风险,引发此异常将杀死您的整个插入。其次,您提出了一个异常,这将停止您的整个插入。

      您需要忽略阻止表中的那些 ID,而不是引发异常。按照您最初的想法,一种方法是利用 NO_DATA_FOUND 异常仅在未找到任何内容时插入。我在你的桌子上创建了一个视图,并在上面定义了一个INSTEAD OF trigger

      我不会使用这种方法(见下文)

      如果我们建立一个测试环境:

      SQL> create table tmp_test ( id number );
      
      Table created.
      
      SQL> create table tmp_blocked ( id number );
      
      Table created.
      
      SQL> insert into tmp_blocked values (3);
      
      1 row created.
      

      然后你可以使用以下内容:

      SQL> create or replace view v_tmp_test as select * from tmp_test;
      
      View created.
      
      SQL> create or replace trigger tr_test
        2   instead of insert on v_tmp_test
        3   for each row
        4
        5  declare
        6
        7     l_id tmp_test.id%type;
        8
        9  begin
       10
       11     select id into l_id
       12       from tmp_blocked
       13      where id = :new.id;
       14
       15  exception when no_data_found then
       16     insert into tmp_test values (:new.id);
       17  end;
       18  /
      
      Trigger created.
      
      SQL> show error
      No errors.
      
      SQL>  insert into v_tmp_test
        2   select level
        3     from dual
        4  connect by level <= 3;
      
      3 rows created.
      
      SQL> select * from tmp_test;
      
              ID
      ----------
               1
               2
      

      正如我所说,我不会使用触发器;一种更有效的方法是使用MERGE。使用与上述相同的设置。

      SQL> merge into tmp_test o
        2  using ( select a.id
        3            from ( select level as id
        4                     from dual
        5                  connect by level <= 3 ) a
        6            left outer join tmp_blocked b
        7              on a.id = b.id
        8           where b.id is null
        9                 ) n
       10     on ( o.id = n.id )
       11   when not matched then
       12    insert values (n.id);
      
      2 rows merged.
      
      SQL>
      SQL> select * from tmp_test;
      
              ID
      ----------
               1
               2
      

      一个更简单的选择是只使用MINUS

       insert into tmp_test
       select level
         from dual
      connect by level <= 3
        minus
       select id
         from tmp_banned
      

      【讨论】:

        猜你喜欢
        • 2016-08-05
        • 1970-01-01
        • 2020-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-11
        • 1970-01-01
        相关资源
        最近更新 更多