для модификации (подмены) значений строк с помощью :NEW следует использовать BEFORE-триггер уровня строки (потому что изменения в атрибутах :NEW возможны только в BEFORE-триггерах);
для проверки (validation) новых значений столбцов обрабатываемой строки следует использовать AFTER-триггер уровня строки.
Вообще говоря, проверки новых данных можно делать и в BEFORE-триггере (:NEW в таких триггерах доступна и на чтение и на запись), однако так делать можно только в том случае, когда BEFORE-триггер один и в нем осуществляется и подмена значений столбцов и их проверка. Порядок срабатывания триггеров одного типа в Oracle до недавнего времени был не определен. Поэтому если триггеров уровня строки на одно событие несколько, то триггер, подменяющий значения столбцов, может сработать и после триггера с проверкой, выставив некорректное с точкой зрения проверки значения (проверка окажется преждевременной). Такой ситуации не возникнет для AFTER-триггеров, которые «видят» псевдозапись :NEW, которая теперь точно никак уже не изменится (изменения строки уже внесены в блоки данных таблицы предложением SQL и поменять строку там еще раз ни в одном AFTER-триггере невозможно). Именно окончательную версию :NEW следует проверить на корректность в AFTER-триггере.
Таким образом, общее правило для триггеров уровня строки такое: «подменяем значения столбцов обрабатываемых строк на новые в BEFORE-триггерах, проверяем новые значения в AFTER-триггерах».
Если триггер реализует реакцию на совершение какого-либо события, то выполнять его правильно после предложения SQL, относящегося к этому событию. Например, если требуется обновлять баланс по итогам добавления нового платежа, то следует делать это AFTER-триггером уже после успешного добавления строки в таблицу платежей, так как баланс логично обновлять только после того, как успешно прошел платеж.
Триггеры в транзакциях
Выполняемые в коде триггера предложения SQL являются частью транзакции, в которую входит предложение SQL, вызвавшее срабатывание триггера. Все предложения SQL в коде триггера выполняются на том же «срезе» базы данных, что и вызвавшее срабатывание триггера предложение SQL. Это распространяется на изменения, внесенные другими транзакциями, их в теле триггера не видно. Если же в ходе выполнения одного предложения SQL происходит несколько срабатываний триггеров, то предложения SQL каждого сработавшего триггера видят изменения, сделанные на предыдущих срабатываниях. Все как всегда – чужие изменения на уровне отдельного предложения SQL не видны и в транзакции всегда видны свои изменения.
Отметим следующие важные обстоятельства:
если в триггере будет инициировано необработанное исключение, то вызвавшее срабатывание триггера предложение SQL завершится с ошибкой и будет выполнена отмена и всех изменений, сделанных предложением SQL, и всех изменений, сделанных всеми триггерами на него (в ходе отмены до неявно установленной точки сохранения перед предложением);
в триггере нельзя выполнять команды фиксации и отмены транзакций COMMIT и ROLLBACK (написать в теле триггера команды COMMIT или ROLLBACK можно – триггер будет успешно создан, но ошибка возникнет на этапе выполнения).
В примере с запретом выдачи пропусков в нерабочее время следует использовать BEFORE-триггер уровня предложения. Отмена изменений происходить не будет, так как не будет самих изменений данных —исключение в триггере будет инициировано еще до выполнения INSERT в таблицу пропусков. Если же в примере с обновлением триггером баланса после поступления платежа произойдет необработанная ошибка в триггере, то сам платеж, на добавление которого сработал триггер, тоже будет отменен – будет отменено добавление строки в таблицу платежей (новая строка платежа «исчезнет»).
Транзакция после ошибки в триггере остается в активном статусе, то есть сама по себе не отменяется и не фиксируется. Просто завершилось с ошибкой одно из входивших в нее предложений SQL, всю транзакцию потом можно будет зафиксировать или отменить. Понятно, что если фиксируется или отменяется транзакция, то это относится и ко всем изменениям, сделанным триггерами, срабатывавшими на предложениях SQL транзакции.
При наличии BEFORE-триггера к строке происходит три обращения (на примере UPDATE):
в режиме согласованного чтения строка отбирается предложением UPDATE для изменения (первое обращение);
выполняется блокирование строки командой SELECT FOR UPDATE (второе обращение);
срабатывает BEFORE-триггер и значения столбцов заблокированной строки передаются в его псевдозапись :OLD;
Читать дальше