Oracle 如何更新一个表并在同一触发器中抛出一个错误,而不引发ORA-04091变异表错误

Oracle 如何更新一个表并在同一触发器中抛出一个错误,而不引发ORA-04091变异表错误,oracle,plsql,oracle11g,database-trigger,Oracle,Plsql,Oracle11g,Database Trigger,我的家庭作业要求我创建一个触发器,在某些情况下,它会更新一个值并引发一个错误。我正在使用一个Oracle数据库,通过终端上的sqlplus命令访问该数据库 我无法使用«:NEW.attributeName:=value»,因为引发错误会阻止更新。 我不能在触发器中使用update语句,因为它将引发一个变异表错误。 根据问题陈述,我应该能够在一个触发器内解决这个问题,问题陈述对此有点含糊不清,但我认为我也不允许使用程序。 我们在课堂上没有看到临时桌子,所以不能用来做家庭作业。然而,我们看到了一些看

我的家庭作业要求我创建一个触发器,在某些情况下,它会更新一个值并引发一个错误。我正在使用一个Oracle数据库,通过终端上的sqlplus命令访问该数据库

我无法使用«:NEW.attributeName:=value»,因为引发错误会阻止更新。 我不能在触发器中使用update语句,因为它将引发一个变异表错误。 根据问题陈述,我应该能够在一个触发器内解决这个问题,问题陈述对此有点含糊不清,但我认为我也不允许使用程序。 我们在课堂上没有看到临时桌子,所以不能用来做家庭作业。然而,我们看到了一些看法。 我尝试使用以下示例中描述的视图。它是用法语写的;它只是说创建一个视图应该允许您使用一个代替触发器,但我确实这样做了,现在当我更新表时,触发器不再被触发

CREATE TABLE CLIENT(
    IDC INTEGER PRIMARY KEY ,
    NOM VARCHAR2 (40));

CREATE TABLE VOYAGE(
    IDV INTEGER PRIMARY KEY ,
    DESTINATION VARCHAR2 (40),
    MAXPLACE INTEGER ) -- nombre total de places     
;

CREATE TABLE INSCRIPTION(
    IDC INTEGER REFERENCES CLIENT(IDC),
    IDV INTEGER REFERENCES VOYAGE(IDV),
    DATERESERV DATE ,
    CONSTRAINT INSCRIPTION_PK PRIMARY KEY (IDC, IDV));

INSERT INTO CLIENT(IDC, NOM) VALUES (1, 'DURAND');
INSERT INTO CLIENT(IDC, NOM) VALUES (2, 'DUBOIS');
INSERT INTO CLIENT(IDC, NOM) VALUES (3, 'DUGENOU');
COMMIT ;

INSERT INTO VOYAGE(IDV, DESTINATION, MAXPLACE) VALUES (10, 'VENISE', 25);
INSERT INTO VOYAGE(IDV, DESTINATION, MAXPLACE) VALUES (11, 'PRAGUE', 20);
COMMIT ;

-- Création d'une vue sur la table INSCRIPTION pour le support des déclencheurs INSTEAD OF
CREATE OR REPLACE VIEW V_INSCRIPTION AS SELECT * FROM INSCRIPTION;

CREATE OR REPLACE TRIGGER TRIG_V_INSCRIPTION INSTEAD OF INSERT ON V_INSCRIPTION FOR EACH ROW 
DECLARE 
    NB_RESERVE INTEGER ; -- nombre de réservations déjà faites
    NB_MAXPLACE INTEGER ; -- nombre de places total

BEGIN 
    SELECT COUNT (*) INTO NB_RESERVE FROM V_INSCRIPTION 
    WHERE IDC=:NEW.IDC
    AND IDV=:NEW.IDV;
    SELECT MAXPLACE INTO NB_MAXPLACE FROM VOYAGE 
    WHERE IDV=:NEW.IDV;
    IF NB_MAXPLACE - NB_RESERVE < 1 THEN 
        DBMS_OUTPUT.PUT_LINE('Désolé, voyage complet');

    ELSE 
        -- dans un déclencheur INSTEAD OF, l'instruction DML sous-jacente ne s'exécute pas. On traite donc l'insertion manuellement
        INSERT INTO INSCRIPTION(IDC, IDV, DATERESERV) VALUES (:NEW.IDC, :NEW.IDV, :NEW.DATERESERV);
    END IF ;
END ;
/

-- DUGENOU aimerait bien aller à Venise :
INSERT INTO INSCRIPTION(IDC, IDV, DATERESERV) SELECT 3, 10, TO_DATE(SYSDATE, 'DD/MM/YYYY') FROM DUAL ;
1 ligne créée.

是否有任何方法可以更新attributeName的值并在不使用过程的情况下引发错误ORA-20101?否则,我就假定我被允许了。

哇。这是一系列要求中的一个。首先,如果希望在引发异常的情况下保留任何更改,则需要创建单独的过程并使用pragma autonomy_transaction语句

第二,如果您正在更新触发触发器的同一个表,并且您只能有一个触发器,那么避免变异表触发器的唯一方法是使用复合触发器。这里有一个指向LiveSQL脚本的链接,该脚本将为您提供大量代码

第三,这完全是个坏主意。DML触发器不应包含DML本身。太多的潜在问题和副作用


相反,创建一个包含所有必要逻辑的过程,并让开发人员在执行更新时调用该过程

事实证明,老师的意思是让我们使用DBMS输出来显示错误消息,而不是实际引发错误。然后,解决方案变为

CREATE OR REPLACE TRIGGER triggerName
BEFORE UPDATE OF attributeName ON TableName
FOR EACH ROW
BEGIN
    IF (:NEW.attributeName < 0) THEN
        :NEW.attributeName := 0;
        DBMS_OUTPUT.PUT_LINE('You cannot update attributeName to a negative value.');
END;
/

你对问题的描述模棱两可。显示的第一个触发器是instead of update触发器,第二个是instead of insert触发器。此外,他们似乎在做完全不同的事情。你到底需要做什么?更新还是插入?什么时候应该提出错误?插入之后还是更新之后?第二个只是我灵感来源的一个例子。实际上,我必须为更新而不是插入编写触发器。无论如何,当老师说我们需要包含一条错误消息时,他们的意思是让我们使用DBMS输出。非常感谢您花时间回答这个问题。事实证明,当老师告诉我们包含错误消息时,他们的意思是打印消息,而不是用消息引发异常。在不引发错误的情况下,问题变得很简单,因为更新前触发器可以将所需的值放入:NEW.attributeName中,而不会使其回滚,因为不会引发错误。谢谢你的链接,它可能会对家庭作业中的其他问题有用。很高兴在这里见到你!
CREATE OR REPLACE TRIGGER triggerName
BEFORE UPDATE OF attributeName ON TableName
FOR EACH ROW
BEGIN
    IF (:NEW.attributeName < 0) THEN
        :NEW.attributeName := 0;
        DBMS_OUTPUT.PUT_LINE('You cannot update attributeName to a negative value.');
END;
/