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

Wab*_*bit 0 oracle triggers plsql

在插入乘客表后尝试触发触发器时,我会收到此错误。此触发器应该调用一个过程,该过程采用新插入值的两个参数,并基于此更新另一个表,即预订表。但是,我收到此错误:

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)
Run Code Online (Sandbox Code Playgroud)

我在 SQL 命令行中编译并测试了该过程,它工作正常。问题似乎出在触发器上。这是触发代码:

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

begin

CALCULATE_FLIGHT_PRICE(:NEW.BOOKING_ID);

end;?????
Run Code Online (Sandbox Code Playgroud)

为什么触发器不调用过程?

Rob*_*ijk 5

您正在以不应该使用的方式使用数据库触发器。数据库触发器尝试读取它当前正在修改的表。如果 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;
/
Run Code Online (Sandbox Code Playgroud)

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

如果您坚持使用数据库触发器,则需要避免游标 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;
Run Code Online (Sandbox Code Playgroud)