PL / SQL DB触发器 - 如果不满足给定条件,则插入失败

时间:2011-06-02 13:41:29

标签: sql oracle plsql triggers

我有一个小测试数据库,我用它来学习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 DBMS)?

2 个答案:

答案 0 :(得分:5)

通常,您不会使用触发器来满足此类要求。相反,你会在桌面上创建一些约束。我会建议(numpilot1numpilot2)的唯一约束以及确保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语句触发器,并执行验证。除了潜在的性能影响之外,还有很多动人的东西需要管理。但是,如果你真的坚持使用触发器解决方案,那么在Tim Hall的网站上有一个using the three trigger solution to work around mutating table exceptions的例子。

答案 1 :(得分:5)

您可以使用基于函数的索引:

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;

结果:数据库损坏。因此,虽然这个触发器可能满足教师,但它是一个糟糕的解决方案,应该使用约束(最好是Justin的解决方案,而不是我的!)