Oracle PL / SQL:从触发器调用过程

时间:2013-12-30 12:57:53

标签: oracle plsql triggers

当我在乘客桌上插入后尝试触发扳机时,我收到此错误。此触发器应该调用一个过程,该过程接受新插入值的两个参数,并基于它更新另一个表,即预订表。但是,我收到了这个错误:

ORA-04091: table AIRLINESYSTEM.PASSENGER is mutating, trigger/function may not see it 
 ORA-06512: at "AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE", line 11 ORA-06512: at 
"AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE", line 15 ORA-06512: at 
 "AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE_T1", line 3 ORA-04088: error during execution of 
  trigger 'AIRLINESYSTEM.CALCULATE_FLIGHT_PRICE_T1' (Row 3)

我在SQL命令行中编写并测试了该过程,它运行正常。问题似乎与触发器有关。这是触发代码:

create or replace trigger "CALCULATE_FLIGHT_PRICE_T1"
AFTER
insert on "PASSENGER"
for each row

begin

CALCULATE_FLIGHT_PRICE(:NEW.BOOKING_ID);

end;​​​​​

为什么触发器没有调用过程?

2 个答案:

答案 0 :(得分:4)

您正在以不应该使用的方式使用数据库触发器。数据库触发器尝试读取当前正在修改的表。如果Oracle允许您这样做,那么您将执行脏读。 幸运的是,Oracle会警告您的行为,并且您可以修改您的设计。

最好的解决方案是创建API。一种程序,最好是一个包装,允许您按照您希望的方式插入乘客。在伪PL / SQL代码中:

procedure insert_passenger
( p_passenger_nr   in number
, p_passenger_name in varchar2
, ...
, p_booking_id     in number
, p_dob            in number
)
is
begin
  insert into passenger (...)
  values
  ( p_passenger_nr
  , p_passenger_name
  , ...
  , p_booking_id
  , p_dob
  );
  calculate_flight_price
  ( p_booking_id
  , p_dob
  );
end insert_passenger;
/

现在调用此过程,而不是insert语句。而你的变异表问题将会消失。

如果您坚持使用数据库触发器,则需要避免使用游标c_passengers中的select语句。这没有任何意义:您刚刚在表乘客中插入一行并知道所有列值。然后调用calculate_flight_price来检索已知的列DOB。 只需在您的calculate_flight_price过程中添加一个参数P_DOB,并使用:new.dob调用它,如下所示:

create or replace trigger calculate_flight_price_t1
after insert on passenger
for each row
begin
  calculate_flight_price
  ( :new.booking_id
  , :new.dob
  );  
end;

答案 1 :(得分:1)

哦,天哪......你正在光标中尝试Dirty Read。这是一个糟糕的设计。 如果允许脏读,它会返回错误的答案,但它也会返回表中从不存在的答案。在多用户数据库中,脏读可能是一个危险的功能。

这里的重点是脏读不是一个特征;相反,这是一种责任。在Oracle数据库中,它只是不需要。你可以获得脏读 - 无阻塞的所有优点 - 没有任何不正确的结果。

阅读"READ UNCOMMITTED isolation level"上允许脏读的更多信息。它提供了基于标准的定义,允许非阻塞读取。

其他方式

您误用了触发器。我的意思是使用错误的触发器。

在表A中插入/更新一行,表A上的触发器(对于每一行)在表A上执行查询(通过程序)?? !!!

Oracle抛出ORA-04091,这是一种预期的正常行为,Oracle希望保护您自己,因为它保证每个语句都是原子的(即要么失败要么完全成功),并且每个语句都看到一致的视图数据

您希望查询(2)不会在(1)上看到插入的行。这将是矛盾的

解决方案: - 使用before代替after

CREATE OR REPLACE TRIGGER SOMENAME
BEFORE INSERT OR UPDATE ON SOMETABLE