Триггеры БД PL / SQL - Ошибка вставки, если данное условие не выполняется - PullRequest
3 голосов
/ 02 июня 2011

У меня есть небольшая тестовая база данных, которую я использую для изучения SQL.
Есть таблица Duel, которая содержит два Pilot внешних ключа (дуэлянты).Я хочу проверить, не встречались ли дуэлянты до «вставки».

Псевдокод:

before insertion on Duel
for each row already in the table
  if ((new_row.numpilot1 = old_row.numpilot1 and new_row.numpilot2 = old_row.numpilot2) OR
      (new_row.numpilot1 = old_row.numpilot2 and new_row.numpilot2 = old_row.numpilot1)
     )
    insertion fails

Еще одна альтернатива будет

tempnum integer;
select numpilot1 into tempnum from duel 
where (:NEW.numpilot1 = numpilot1 and :NEW.numpilot2 = numpilot2) OR
      (:NEW.numpilot1 = numpilot2 and :NEW.numpilot2 = numpilot1);

if tempnum == null
    fail insertion

Что такоеPL / SQL (Oracle СУБД) версия этого?

Ответы [ 2 ]

5 голосов
/ 02 июня 2011

Вы можете использовать индекс на основе функции:

create unique index duel_uk on duel
( least(numpilot1, numpilot2), greatest(numpilot1, numpilot2));

Ответ, который вы явно ищете, - это триггер, подобный этому:

create trigger duel_trg
before insert on duel
for each row
declare
  dummy number;
begin
  select count(*)
  into dummy
  from duel
  where (numpilot1 = :new.numpilot1 and numpilot2 = :new.numpilot2)
  or (numpilot1 = :new.numpilot2 and numpilot2 = :new.numpilot1);

  if dummy > 0 then
    raise_application_error(-20001,'You lose');
  end if;
end;

Однако это не сможет обеспечить целостность в многопользовательской (или многосессионной) среде, поскольку это может произойти:

User1> insert into duel (numpilot1, numpilot2) values (1,2);
-- trigger checks, all seems OK

User2> insert into duel (numpilot1, numpilot2) values (1,2);
-- trigger checks, all seems OK (can't see User1's new row
-- as it hasn't been committed)

User1> commit;
User2> commit;

Результат: повреждена база данных. Поэтому, хотя этот триггер может удовлетворить учителя, это плохое решение, и вместо него следует использовать ограничения (желательно решение Джастина, а не мое!)

5 голосов
/ 02 июня 2011

Обычно вы не используете триггер для такого рода требований. Вместо этого вы бы создали пару ограничений на столе. Я бы предложил уникальное ограничение на (numpilot1, numpilot2) вместе с проверочным ограничением, которое гарантирует, что numpilot1 < numpilot2.

ALTER TABLE duel
  ADD CONSTRAINT unique_pilot_combination
          UNIQUE( numpilot1, numpilot2 );

ALTER TABLE duel
  ADD CONSTRAINT chk_pilot1_lt_pilot2
           CHECK( numpilot1_fk < numpilot2_fk );

Если бы вы хотели сделать что-то подобное в триггере, это было бы немного сложнее. В общем случае триггер уровня строки в DUEL не может запросить таблицу DUEL, так как это приведет к исключению мутирующей таблицы. Вам потребуется создать коллекцию в пакете, триггер оператора before, который инициализирует коллекцию, триггер уровня строки, который вставляет новые ключи пилот-сигнала в коллекцию, и триггер оператора after, который считывает данные в коллекции и выполняет Проверка. Это довольно много движущихся частей в дополнение к потенциальному снижению производительности. Если вы действительно застряли с триггерным решением, однако, есть пример , использующий триггерное решение для обхода исключений мутировавшей таблицы на сайте Тима Холла.

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