1

我有三张桌子,电影,表演和房间。我想在给定的房间里为一部电影插入新的节目,我想我可以用一个触发器来做到这一点,如果没有与现有的时间冲突,我可以用一个触发器来完成。

我写了这个:

CREATE OR REPLACE TRIGGER insert_new_show
BEFORE INSERT ON show
FOR EACH ROW
DECLARE
   -- pragma autonomous_transaction;
   collisioni varchar(40);
   runtime INT;
   error1 EXCEPTION;
   error2 EXCEPTION;
BEGIN
   IF  :new.time < CURRENT_TIMESTAMP THEN
      RAISE error1;
   END IF;  

   SELECT runtime INTO runtime 
     FROM film 
    WHERE title = deref(:new.di_film).title;

   SELECT film.title INTO collisioni 
     FROM show JOIN film ON deref(show.di_film).title=film.title
    WHERE DEREF(room).nome = DEREF(:NEW.room).nome 
      AND ( (:new.time < show.time AND :new.time + runtime + 10 > show.time) 
            OR (:new.time > show.time 
                 AND :new.time < show.time + runtime + 10));

   IF NOT SQL%NOTFOUND THEN
      INSERT INTO show
       SELECT to_timestamp(:NEW.orario,'yyyy-mm-dd hh24:mi:ss')
            , :NEW.max_n_spot,:NEW.costo, REF(s), NULL, REF(f)
         FROM film f, room s
        WHERE f.title=deref(:NEW.di_film).title 
          AND s.nome=deref(:NEW.room).nome;     
   ELSE
      RAISE error2; 
   END IF;

EXCEPTION 
   WHEN error1 THEN
      RAISE_APPLICATION_ERROR(-20491,'Error');
   WHEN error2 THEN
      RAISE_APPLICATION_ERROR(-20491,'Error');

END;

如果我不使用“pragma automatic_transaction”,我会收到table s%s is mutating错误消息。但是,当然对于事务,触发器看不到:new值。我还想用 CHECK CONSTRAINT 进行检查,但我不知道它是否有效。你能帮我找到解决办法吗?

4

1 回答 1

0

这是经典的“使用包/过程”情况。

ORA-04091: table name is mutating, trigger/function may not see it当您查询当前正在对其执行 DML 操作的触发器中的表时,您会得到。Oracle 不允许这样做以维护数据的读取一致性视图。有多种方法可以避免这种情况,其中大多数涉及尝试做一些有点时髦的事情;包括您已经注意到的自主事务。

但是,它们不会影响错误的根本原因;也就是说,你做错了什么。此错误通常表示以下两种情况中的一种或两种:

  • 您将太多逻辑放入触发器中
  • 您的数据模型有缺陷。

解决方案是将逻辑移动到包或过程中,这具有消除混淆层和帮助调试的额外好处。

我在这里看不到检查约束是如何可能的,因为您仍然需要知道 FILM 的运行时间,这需要查询第二个表。

进一步阅读:

于 2012-11-02T13:47:50.730 回答