Как обновить таблицу и сгенерировать ошибку в том же триггере, не вызывая ошибку мутационной таблицы ORA-04091 - PullRequest
1 голос
/ 27 апреля 2019

Моя домашняя работа требует, чтобы я создал один триггер, который при определенных обстоятельствах обновляет значение И вызывает ошибку.Я использую базу данных 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 AND и вызвать ошибкуORA-20101 без использования процедуры? Иначе, я предполагаю, что мне позволено.

Ответы [ 2 ]

2 голосов
/ 28 апреля 2019

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

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

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

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

0 голосов
/ 28 апреля 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;
/
...