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

F. *_* P. 3 sql oracle triggers plsql

我有一个小的测试数据库,我用它来学习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
Run Code Online (Sandbox Code Playgroud)

另一种选择是

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
Run Code Online (Sandbox Code Playgroud)

什么是PL/SQL(Oracle DBMS)版本?

Jus*_*ave 5

通常,您不会使用触发器来满足此类要求.相反,你会在桌面上创建一些约束.我建议对(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 );
Run Code Online (Sandbox Code Playgroud)

如果你想在触发器中做这种事情,那将会更加复杂.通常,行级触发器DUEL无法查询DUEL表 - 这样做会创建变异表异常.您需要在包中创建一个集合,一个初始化集合的before语句触发器,一个将新的导频键插入集合的行级触发器,以及一个读取集合中数据的after语句触发器,并执行验证.除了潜在的性能影响之外,还有很多动人的东西需要管理.但是,如果您真的坚持使用触发器解决方案,那么可以使用三个触发器解决方案来解决 Tim Hall网站上的变异表异常问题.

  • @FranciscoP - 您所遵循的指南要么是由nincompoop编写,要么不是要在Oracle数据库上实现.约束(或Tony的基于函数的索引在逻辑上是相同的)是唯一合理的解决方案.这不仅仅是性能问题,也是*正确性*.基于触发器的解决方案不适用于多用户环境. (3认同)

Ton*_*ews 5

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

create unique index duel_uk on duel
( least(numpilot1, numpilot2), greatest(numpilot1, numpilot2));
Run Code Online (Sandbox Code Playgroud)

您正在寻找的答案是这样的触发器:

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;
Run Code Online (Sandbox Code Playgroud)

但是,这将无法确保多用户(或多会话)环境中的完整性,因为这可能发生:

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;
Run Code Online (Sandbox Code Playgroud)

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

  • 你明智地选择了! (2认同)