Как обновить таблицу И выдать ошибку в том же триггере, не вызывая ошибку изменяющейся таблицы ORA-04091

Моя домашняя работа требует, чтобы я создал единственный триггер, который при определенных обстоятельствах обновляет значение И вызывает ошибку. Я использую базу данных Oracle, к которой я обращаюсь через команду sqlplus на терминале.

  • Я не могу использовать «: NEW.attributeName: = value», потому что появление ошибки препятствует обновлению.
  • Я не могу использовать оператор обновления внутри триггера, потому что он вызовет ошибку изменяющейся таблицы.
  • Согласно постановке проблемы, я должен уметь решить эту проблему с помощью одного триггера, и формулировка проблемы довольно расплывчата, но я думаю, что мне также не разрешено использовать процедуру.
  • Мы не видели временных столов в классе, поэтому их нельзя использовать для выполнения домашних заданий. Однако мы видели взгляды.
CREATE OR REPLACE TRIGGER triggerName
INSTEAD OF UPDATE OF attributeName ON TableName
FOR EACH ROW
BEGIN
    IF (:NEW.attributeName < 0) THEN
        UPDATE  TableName
        SET attributeName = 0
        WHERE   attr0 = :NEW.attr0 AND
            attr1 = :NEW.attr1
        ;
        raise_application_error(-20101, 'You cannot update attributeName to a negative value.');
END;
/

Я попытался использовать представление, как описано в следующем примере (https://sgbd.developpez.com/oracle/ora-04091/#LI). Это на французском; он просто говорит, что создание представления должно позволить вам использовать триггер INSTEAD OF, но я сделал именно это, и теперь триггер больше не запускается, когда я обновляю таблицу.

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 без использования процедуры? В противном случае, я полагаю, мне разрешено.


person AGA    schedule 27.04.2019    source источник
comment
Ваше описание проблемы неоднозначно. Первый показанный вами триггер - это instead of update, а второй - instead of insert. Более того, похоже, что они делают совершенно разные вещи. Что именно нужно делать? обновить или вставить? Когда должна появиться ошибка? после вставки или после обновления?   -  person Kaushik Nayak    schedule 28.04.2019
comment
Второй был просто примером, на котором я черпал вдохновение. На самом деле мне нужно написать триггер для обновления, а не для вставки. В любом случае оказывается, что учитель имел в виду, что мы должны использовать вывод СУБД, когда они сказали, что нам нужно включить сообщение об ошибке.   -  person AGA    schedule 28.04.2019


Ответы (2)


Вот это да. Это чертовски много требований. Прежде всего, если вы хотите, чтобы какие-либо из ваших изменений были сохранены даже при возникновении этого исключения, вам нужно создать отдельную процедуру и использовать инструкцию pragma autonomous_transaction.

Во-вторых, единственный способ избежать триггера изменяющейся таблицы, если вы обновляете ту же таблицу, для которой срабатывает триггер, И у вас может быть только один триггер, - это использовать составной триггер. Вот ссылка на сценарий LiveSQL, который предоставит вам много кода для работы. https://livesql.oracle.com/apex/livesql/file/content_CGRC9SJRBTH83GTAAWUB1H4JG.html

В-третьих, все это плохая идея. Триггер DML не должен содержать самого DML. Слишком много потенциальных проблем и побочных эффектов.

Вместо этого создайте процедуру, которая содержит всю необходимую логику, и пусть разработчики вызывают ее при выполнении обновления.

person Steven Feuerstein    schedule 28.04.2019
comment
Большое спасибо за то, что нашли время ответить на этот вопрос. Оказывается, когда учитель сказал нам включить сообщение об ошибке, они имели в виду распечатать сообщение, а не вызывать исключение с сообщением. Без возникновения ошибки проблема становится тривиальной, поскольку триггер обновления BEFORE может поместить желаемое значение в: NEW.attributeName и не иметь отката, потому что ошибка не возникает. Спасибо и за ссылку, она может пригодиться для ответа на один из других вопросов в домашнем задании. - person AGA; 28.04.2019
comment
Рад видеть вас здесь через некоторое время! - person Kaushik Nayak; 28.04.2019

Оказывается, учитель имел в виду, что мы должны использовать выходные данные СУБД для отображения «сообщения об ошибке», а не для фактического сообщения об ошибке. Затем решение становится

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;
/
person AGA    schedule 28.04.2019