【问题标题】:Oracle PL/SQL: Calling a procedure from a triggerOracle PL/SQL:从触发器调用过程
【发布时间】:2013-12-30 12:57:53
【问题描述】:

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

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;​​​​​

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

【问题讨论】:

  • ORA-04091 告诉您,如果表 PASSENGERS 在过程 CALCULATE_FLIGHT_PRICE 中,您正在修改/查询数据 - 这是非法的。
  • @haki 我更新了触发器,仍然得到同样的错误
  • @Rachcha 我发布了程序
  • @haki 说的是您正在查询与您有触发器相同的内容。因此,在您在 PASSENGER 表上插入数据后,您的触发器正在查询 SELECT dob from passenger WHERE booking_id = p_bookingid; 上的 sable 表,这只是非法的数据尚未提交,您正在尝试从中获取数据。
  • @Wabbit- 这就是问题所在!您正在向passengers 表中插入一行,在该表上执行一个触发器,该触发器调用一个存储过程,该过程又从“乘客”表中调用SELECTs。 SELECTing 在运行 DML 的表时是被禁止的。您不能从触发器中的任何位置以及从触发器调用的任何过程或函数对 passengers 表发出 SELECT

标签: oracle plsql triggers


【解决方案1】:

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

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

如果您坚持使用数据库触发器,那么您需要避免在游标 c_passengers 中使用 select 语句。这没有任何意义:您刚刚在表乘客中插入了一行并且知道所有列值。然后调用 calculate_flight_price 来检索您已经知道的 DOB 列。 只需将参数 P_DOB 添加到您的 calculate_flight_price 过程并使用 :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;

【讨论】:

    【解决方案2】:

    哦,天哪...您正在光标中尝试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
    

    【讨论】:

      猜你喜欢
      • 2011-09-19
      • 1970-01-01
      • 2012-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-28
      • 2016-07-22
      相关资源
      最近更新 更多