我有一个小测试数据库,我用它来学习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)?
答案 0 :(得分: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 );
如果你想在触发器中做这种事情,那将会更加复杂。通常,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的解决方案,而不是我的!)