Oracle: использование подзапроса в триггере

Как я могу обойти ограничение Oracle, запрещающее подзапросы в триггерах.

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

CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW WHEN (old.archiving_status <> new.archiving_status
  AND new.archiving_status = 1
  AND (SELECT offer FROM projects WHERE projnum = :new.projnum) IS NULL
)
BEGIN
  INSERT INTO offer_log (offer, status, date)
  VALUES (null, 9, sysdate);
END;

person vipirtti    schedule 26.05.2009    source источник


Ответы (3)


Этот триггер сделает это:

CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW WHEN (old.archiving_status <> new.archiving_status
  AND new.archiving_status = 1
)
DECLARE
  l_offer projects.offer%TYPE;
BEGIN
  SELECT offer INTO l_offer 
  FROM projects 
  WHERE projnum = :new.projnum;

  IF l_offer IS NULL THEN
    INSERT INTO offer_log (offer, status, date)
    VALUES (null, 9, sysdate);
  END IF;
END;

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

person Tony Andrews    schedule 26.05.2009
comment
Просто предупреждение о том, что если вы используете многотабличные вставки (вставка как в проект, так и в проект_архивирование), вы можете получить ошибку мутирующей таблицы. Таким образом, предпочтительнее разместить логику там, где находится исходная вставка, а не полагаться на триггер. - person Gary Myers; 27.05.2009

Я ожидаю, что вы хотите что-то вроде

CREATE OR REPLACE TRIGGER trigger_w_subquery
AFTER UPDATE OR INSERT ON project_archiving
FOR EACH ROW 
WHEN (old.archiving_status <> new.archiving_status
  AND new.archiving_status = 1)
DECLARE
  l_offer projects.offer%TYPE;
BEGIN
  SELECT offer 
    INTO l_offer
    FROM projects 
   WHERE projnum = :new.projnum;

  IF( l_offer IS NULL )
  THEN
    INSERT INTO offer_log (offer, status, date)
      VALUES (null, 9, sysdate);
  END IF;
END;
person Justin Cave    schedule 26.05.2009
comment
Извини, Джастин. Я могу выбрать только один ответ как правильный. На этот раз вы были избиты простым предупреждением об исключении :-) Голосование, которое вы заслужили, тем не менее. - person vipirtti; 26.05.2009

Можете ли вы поместить условие в действие (между BEGIN и END), а не в «будет ли оно срабатывать»? Да, это означает, что тело триггера может срабатывать чаще, но если это решит проблему...

person Jonathan Leffler    schedule 26.05.2009
comment
Стоимость выполнения подзапроса, скорее всего, намного выше, чем накладные расходы на срабатывание триггера, поэтому я думаю, что в любом случае это не имеет значения. - person Erich Kitzmueller; 26.05.2009
comment
Попытка НАЧАТЬ, ЕСЛИ (ВЫБРАТЬ предложение ИЗ проектов, ГДЕ projnum = :new.projnum) IS NULL THEN INSERT INTO и т. д. Однако Oracle выдает ошибку, когда встречает SELECT в этом операторе. Просто не ожидает. - person vipirtti; 26.05.2009
comment
Я недостаточно знаком с PL/SQL, чтобы понять, правдоподобно ли это писать код в триггере. Это выглядит хорошо; другие системы, которые я знаю, требуют некоторого присваивания из SELECT (с предложением INTO и переменной), а затем проверяют переменную или что-то в этом роде. Вероятно, в руководствах есть описание ограничений на триггеры. Кроме того, можете ли вы вызвать процедуру в части действия? Если да, то, может быть, это сработает для вас? (Передайте соответствующие значения в качестве параметров.) - person Jonathan Leffler; 26.05.2009