一般に、そのトリガーは機能しません。
一般に、テーブル X の行レベル トリガーはテーブル X に対してクエリを実行できません。したがって、この場合、行レベル トリガーRENT
は通常、テーブルのクエリを実行できません。これRENT
を行うと、変更トリガー例外がスローされます。アプリケーションがステートメントを使用して一度に 1 行しか挿入しないことを保証したい場合はINSERT ... VALUES
、ミューティング トリガー エラーに遭遇することはありませんが、これは一般的に適切な制限ではありません。また、マルチユーザー環境では適切ではありません。ほぼ同時に実行されている 2 つのトランザクションが両方とも同じユーザーに対して書籍をチェックアウトしている場合、このトリガーにより、両方のトランザクションが成功する可能性があります。
この種のチェックを追加する適切な場所は、ほぼ確実に、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
保持しているコレクション内にあるという追加の条件があります。
この「3 トリガー ソリューション」は、特に適切なソリューションが最初にトリガーを使用しない場合に、システムにかなりの複雑さを追加します。