Триггер уровня оператора для применения ограничения - PullRequest
0 голосов
/ 01 июня 2019

Я пытаюсь реализовать триггер уровня выписки, чтобы применить следующее «Заявитель не может подать заявку более чем на две позиции за один день».

Я могу применить его с помощью триггера уровня строки (какпоказано ниже), но я понятия не имею, как это сделать, используя триггер уровня оператора, когда я не могу использовать: NEW или: OLD.

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

CREATE TABLE APPLIES(
anumber     NUMBER(6)   NOT NULL,  /* applicant number */
pnumber     NUMBER(8)   NOT NULL, /* position number */
appDate     DATE        NOT NULL, /* application date*/
CONSTRAINT APPLIES_pkey PRIMARY KEY(anumber, pnumber)
);

CREATE OR REPLACE TRIGGER app_trigger
BEFORE INSERT ON APPLIES
FOR EACH ROW
DECLARE 
  counter NUMBER;
BEGIN
  SELECT COUNT(*) INTO counter 
  FROM APPLIES 
  WHERE anumber = :NEW.anumber
  AND to_char(appDate, 'DD-MON-YYYY') = to_char(:NEW.appDate, 'DD-MON-YYYY');

  IF counter = 2 THEN
      RAISE_APPLICATION_ERROR(-20001, 'error msg');
  END IF;
END;

Ответы [ 2 ]

1 голос
/ 01 июня 2019

Вы правы, что у вас нет значений: OLD и: NEW - поэтому вам нужно проверить всю таблицу, чтобы увидеть, является ли условие (давайте не будем называть это «ограничением», так как этот термин имеет конкретное значение в смысл реляционной базы данных) был нарушен:

CREATE OR REPLACE TRIGGER APPLIES_AIU
  AFTER INSERT OR UPDATE ON APPLIES
BEGIN
  FOR aRow IN (SELECT ANUMBER,
                      TRUNC(APPDATE) AS APPDATE,
                      COUNT(*) AS APPLICATION_COUNT
                 FROM APPLIES
                 GROUP BY ANUMBER, TRUNC(APPDATE)
                 HAVING COUNT(*) > 2)
  LOOP
    -- If we get to here it means we have at least one user who has applied
    -- for more than two jobs in a single day.

    RAISE_APPLICATION_ERROR(-20002, 'Applicant ' || aRow.ANUMBER ||
                                    ' applied for ' || aRow.APPLICATION_COUNT ||
                                    ' jobs on ' ||
                                    TO_CHAR(aRow.APPDATE, 'DD-MON-YYYY'));
  END LOOP;
END APPLIES_AIU;

Рекомендуется добавить индекс для поддержки этого запроса, чтобы он работал эффективно:

CREATE INDEX APPLIES_BIU_INDEX
  ON APPLIES(ANUMBER, TRUNC(APPDATE));

dbfiddle здесь

Удачи.

1 голос
/ 01 июня 2019

Ваше правило включает более одной строки одновременно. Таким образом, вы не можете использовать триггер FOR ROW LEVEL: запрос к APPLIES, как вы предлагаете, вызовет ORA-04091: таблица является мутирующим исключением.

Итак, ПОСЛЕ утверждения, что это так.

CREATE OR REPLACE TRIGGER app_trigger
AFTER INSERT OR UPDATE ON APPLIES
DECLARE 
  cursor c_cnt is
    SELECT 1 INTO counter 
    FROM APPLIES 
    group by anumber, trunc(appDate) having count(*) > 2;
  dummy number;
BEGIN
  open c_cnt;
  fetch c_cnt in dummy;
  if c_cnt%found then 
      close c_cnt;
      RAISE_APPLICATION_ERROR(-20001, 'error msg');
  end if;
  close c_cnt;
END;

Очевидно, что запрос всей таблицы будет неэффективным в масштабе. (Одна из причин, почему триггеры не рекомендуются для такого рода вещей). Так что это ситуация, в которой мы могли бы использовать составной триггер (при условии, что мы находимся на 11g или позже).

...