通常,该触发器不起作用。
通常,表 X 上的行级触发器无法查询表 X。因此,在您的情况下,RENT
通常不允许行级触发器查询表 -RENT
这样做会引发 mutating trigger 异常。如果您想保证您的应用程序一次只使用一条INSERT ... VALUES
语句插入 1 行,您将不会遇到变异触发器错误,但这通常不是适当的限制。在多用户环境中也不合适——如果有两个事务几乎同时运行,都向同一个用户签出一本书,这个触发器将潜在地允许两个事务成功。
添加此类检查的适当位置几乎可以肯定是在创建RENT
记录的存储过程中。该存储过程应该检查会员在当前月份有多少租金,如果超过限制,则会出错。就像是
CREATE OR REPLACE PROCEDURE rent_book( p_member IN rent.member%type,
p_book IN rent.book%type )
AS
l_max_rentals_per_month constant number := 2;
type rental_nt is table of rent.rend_id%type;
l_rentals_this_month rental_nt;
BEGIN
SELECT rent_id
BULK COLLECT INTO l_rentals_this_month
FROM rent
WHERE member = p_member
AND trunc(rental_date,'MM') = trunc(sysdate, 'MM')
FOR UPDATE;
IF( l_rentals_this_month.count > l_max_rentals_per_month )
THEN
RAISE_APPLICATION_ERROR( -20001, 'Rental limit exceeded' );
ELSE
INSERT INTO rent( rent_id, member, book, rental_date )
VALUES( rent_id_seq.nextval, p_member, p_book, sysdate );
END IF;
END;
如果您真的想使用触发器强制执行类似的操作,那么解决方案会变得更加复杂。如果你不关心效率,你可以创建一个语句级触发器
create or replace trigger tr_rent
after insert on rent
declare
v_count number;
begin
select count(id)
into v_count
from (select member, count(*)
from rent
where trunc(rental_date,'MM') = trunc(sysdate,'MM')
group by member
having count(*) > 2);
if v_count >= 1 then
raise_application_error (-20001, 'At least one person has exceeded their rental limit');
end if;
end;
这可行,但它(至少)需要您每次都对每个成员进行验证。当您拥有大量成员时,这是非常低效的。您可以通过大幅增加复杂性来减少工作量。如果你
- 创建一个包,该包声明一个包全局变量,该变量是
rent.member%type
.
- 创建一个初始化此集合的 before 语句触发器。
:new.member
创建一个添加到此集合的行级触发器
- 创建一个与上述类似的 after 语句触发器,但它有一个附加条件,即
member
在您正在维护的集合中。
这种“三触发器解决方案”给系统增加了大量的复杂性,特别是在适当的解决方案首先不使用触发器的情况下。