Oracle - проблема создания триггера, который обновляет другую таблицу - PullRequest
3 голосов
/ 22 сентября 2011

Я прочитал документы Oracle по созданию триггеров и делаю все именно так, как он показывает, однако это просто не работает.Моя цель - обновить таблицу TPM_PROJECT, указав минимальное значение STARTDATE в таблице TPM_TRAININGPLAN.Таким образом, каждый раз, когда кто-то обновляет столбец STARTDATE в TPM_TRAININGPLAN, я хочу обновить таблицу TPM_PROJECT.Вот что я пытаюсь:

CREATE TRIGGER Trigger_UpdateTrainingDelivery
    AFTER DELETE OR INSERT OR UPDATE OF STARTDATE
    ON TPM_TRAININGPLAN
    FOR EACH ROW WHEN (new.TRAININGPLANTYPE='prescribed')
    BEGIN
       UPDATE TPM_PROJECT SET TRAININGDELIVERYSTART = (SELECT MIN(TP.STARTDATE) FROM TPM_TRAININGPLAN TP WHERE TP.PROJECTID = new.PROJECTID AND TP.TRAININGPLANTYPE='prescribed')
       WHERE PROJECTID = new.PROJECTID
    END;

Триггер создан без ошибок, но я получаю предупреждение:

 Warnings: ---> 
   W (1): Warning: execution completed with warning
          <--- 

Конечно, Oracle не достаточно хорош, чтобы на самом делескажите мне, что за предупреждение, мне просто показывают, что оно есть.

Далее, если я обновлю таблицу плана тренировок с помощью:

UPDATE TPM_TRAININGPLAN
set STARTDATE = to_date('03/12/2009','mm/dd/yyyy')
where TRAININGPLANID=15916;

Я получу сообщение об ошибке:

>[Error] Script lines: 20-22 ------------------------
 ORA-04098: trigger 'TPMDBO.TRIGGER_UPDATETRAININGDELIVERY' is invalid and failed re-validation
 Script line 20, statement line 1, column 7 

Есть идеи, что я делаю не так?Спасибо!

Ответы [ 2 ]

4 голосов
/ 22 сентября 2011

Несколько проблем в произвольном порядке.

Во-первых, в теле триггера уровня строки необходимо использовать :new и :old для ссылки на новые и старые записи.Ведущая толстая кишка необходима.Таким образом, ваше предложение WHERE должно быть

WHERE PROJECTID = :new.PROJECTID

Во-вторых, если вы используете CREATE TRIGGER в SQL * Plus, вы можете получить список ошибок и предупреждений, используя команду SHOW ERRORSто есть

SQL> show errors

Вы также можете запросить таблицу DBA_ERRORS (или ALL_ERRORS или USER_ERRORS в зависимости от вашего уровня привилегий), но это не то, к чему вам обычно нужно прибегать.

В-третьих, если исправить синтаксические ошибки, вы получите ошибку таблицы мутаций , если будете использовать эту логику.Триггер уровня строки в таблице A (TPM_TRAININGPLAN в данном случае) не может запросить таблицу A, поскольку таблица может находиться в несогласованном состоянии.Обойти это можно, как показывает Тим ​​в своей статье, создав пакет с коллекцией, инициализировав эту коллекцию в триггере оператора before, заполнив данные в коллекции триггером на уровне строк, а затем обработав измененные строки втриггер после оператора.Однако это довольно сложная задача для добавления в систему, поскольку вам придется управлять несколькими различными объектами.

Как правило, вам лучше реализовать эту логику как часть любого API, который вы используете дляманипулировать таблицей TPM_TRAININGPLAN.Если это хранимая процедура, имеет больше смысла помещать логику для обновления TPM_PROJECT в эту хранимую процедуру, а не в триггер.Общеизвестно, что больно пытаться отлаживать приложение, которое имеет много логики, встроенной в триггеры, потому что это очень затрудняет разработчикам точное отслеживание того, какие операции выполняются.Кроме того, вы можете удалить столбец TRAININGDELIVERYSTART из таблицы TPM_PROJECT и просто вычислить минимальную дату начала во время выполнения.

В-четвертых, если ваш триггер срабатывает при вставках, обновлениях и удалениях, вы не можете простоссылка :new значения.:new действительно для вставок и обновлений, но будет иметь значение NULL, если вы делаете удаление.:old действительно для удалений и обновлений, но будет иметь значение NULL, если вы делаете вставку.Это означает, что вам, вероятно, нужно иметь логику в духе (ссылаясь на пакетное решение Тима)

BEGIN
  IF inserting 
  THEN
    trigger_api.tab1_row_change(p_id => :new.projectid, p_action => 'INSERT');
  ELSIF updating
  THEN
    trigger_api.tab1_row_change(p_id => :new.projectid, p_action => 'UPDATE');
  ELSIF deleting
  THEN
    trigger_api.tab1_row_change(p_id => :old.projectid, p_action => 'DELETE');
  END IF;
END;
2 голосов
/ 22 сентября 2011

Как предложил Джастин Кейв, вы можете рассчитать минимальную дату начала, когда вам это нужно. Это может помочь, если вы создадите индекс для (projectid, startdate);

Если у вас действительно много проектов и планов обучения, другим решением может быть создание МАТЕРИАЛИЗИРОВАННОГО ВИДА, содержащего все необходимые данные:

CREATE MATERIALIZED VIEW my_view
... add refresh options here ...
AS
SELECT t.projectid,  MIN(t.start_date) AS min_start_date
FROM TPM_TRAININGPLAN t
GROUP BY t.projectid;

(извините, Oracle не работает, приведенный выше код только для справки)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...