0

私のトリガーは、「新しい」マネージャーが 5 人以下の従業員を監督しているかどうかを確認したいと考えています。BLOCKED_MANAGER テーブル (ssn,numberofemployees) には、5 人だけを監督するマネージャーがいます。最後に、すべての更新は SUPERLOG テーブル (日付、ユーザー、old_manager、new_manager) に記録されます。トリガーに関するコンパイル エラーは発生しませんが、superssn を更新すると次のエラーが発生します。

SQL> update employee set superssn='666666607' where ssn='111111100';
update employee set superssn='666666607' where ssn='111111100'
   *
ERROR at line 1:
ORA-04091: Table FRANK.EMPLOYEE is mutating, the trigger/function
can't read it
ORA-06512: a "FRANK.TLOG", line 20
ORA-04088: error during execution of trigger 'FRANK.TLOG'

このトリガーを解決するにはどうすればよいですか? ありがとうございました

create or replace trigger tlog 
before update of superssn on employee
for each row
declare
t1 exception;
n number:=0;
cont number:=0;
empl varchar2(16);
cursor cur is (select ssn from blocked_manager where ssn is not null);
begin
open cur;
    loop
fetch cur into empl;
exit when cur%notfound;
if(:new.superssn = empl) then
    n:=1;
end if;
end loop;
close cur;
if n=1 then
raise t1;
end if;
select count(*) into cont from employee group by superssn having superssn=:new.superssn;
if(cont=4) then
insert into blocked_manager values(:new.superssn,5);
end if;
insert into superlog values(sysdate,user,:old.superssn, :new.superssn );
exception
when t1 then
raise_application_error(-20003,'Manager '||:new.superssn||' has already 5 employees');
end;
4

2 に答える 2

0

お気づきのように、行レベルのトリガーが定義されているのと同じテーブルから選択することはできません。テーブル変更例外が発生します。

トリガーを使用してこの検証を適切に作成するには、マルチユーザー環境で検証を正しくシリアル化できるように、ユーザー指定のロックを取得する手順を作成する必要があります。

PROCEDURE request_lock
  (p_lockname                     IN     VARCHAR2
  ,p_lockmode                     IN     INTEGER  DEFAULT dbms_lock.x_mode
  ,p_timeout                      IN     INTEGER  DEFAULT 60
  ,p_release_on_commit            IN     BOOLEAN  DEFAULT TRUE
  ,p_expiration_secs              IN     INTEGER  DEFAULT 600)
IS
  -- dbms_lock.allocate_unique issues implicit commit, so place in its own
  -- transaction so it does not affect the caller
  PRAGMA AUTONOMOUS_TRANSACTION;
  l_lockhandle                   VARCHAR2(128);
  l_return                       NUMBER;
BEGIN
  dbms_lock.allocate_unique
    (lockname                       => p_lockname
    ,lockhandle                     => p_lockhandle
    ,expiration_secs                => p_expiration_secs);
  l_return := dbms_lock.request
    (lockhandle                     => l_lockhandle
    ,lockmode                       => p_lockmode
    ,timeout                        => p_timeout
    ,release_on_commit              => p_release_on_commit);
  IF (l_return not in (0,4)) THEN
    raise_application_error(-20001, 'dbms_lock.request Return Value ' || l_return);
  END IF;
  -- Must COMMIT an autonomous transaction
  COMMIT;
END request_lock;

この手順は、複合トリガーで使用できます (少なくとも Oracle 11 を想定し、以前のバージョンでは個々のトリガーに分割する必要があります)。

CREATE OR REPLACE TRIGGER too_many_employees
  FOR INSERT OR UPDATE ON employee
  COMPOUND TRIGGER

  -- Table to hold identifiers of inserted/updated employee supervisors
  g_superssns sys.odcivarchar2list;

BEFORE STATEMENT 
IS
BEGIN
  -- Reset the internal employee supervisor table
  g_superssns := sys.odcivarchar2list();
END BEFORE STATEMENT; 

AFTER EACH ROW
IS
BEGIN
  -- Store the inserted/updated supervisors of employees
  IF (  (   INSERTING
        AND :new.superssn IS NOT NULL)
     OR (   UPDATING
        AND (  :new.superssn  <> :old.superssn
            OR :new.superssn IS NOT NULL AND :old.superssn IS NULL) ) )
  THEN           
    g_superssns.EXTEND;
    g_superssns(g_superssns.LAST) := :new.superssn;
  END IF;
END AFTER EACH ROW;

AFTER STATEMENT
IS
  CURSOR csr_supervisors
  IS
    SELECT DISTINCT
           sup.column_value superssn
    FROM TABLE(g_superssns) sup
    ORDER BY sup.column_value;
  CURSOR csr_constraint_violations
    (p_superssn employee.superssn%TYPE)
  IS
    SELECT count(*) employees
    FROM employees
    WHERE pch.superssn = p_superssn
    HAVING count(*) > 5;
  r_constraint_violation csr_constraint_violations%ROWTYPE;
BEGIN
  -- Check if for any inserted/updated employee there exists more than
  -- 5 employees for the same supervisor. Serialise the constraint for each
  -- superssn so concurrent transactions do not affect each other
  FOR r_supervisor IN csr_supervisors LOOP
    request_lock('TOO_MANY_EMPLOYEES_' || r_supervisor.superssn);
    OPEN csr_constraint_violations(r_supervisor.superssn);
    FETCH csr_constraint_violations INTO r_constraint_violation;
    IF csr_constraint_violations%FOUND THEN
      CLOSE csr_constraint_violations;
      raise_application_error(-20001, 'Supervisor ' || r_supervisor.superssn || ' now has ' || r_constraint_violation.employees || ' employees');
    ELSE
      CLOSE csr_constraint_violations;
    END IF;
  END LOOP;
END AFTER STATEMENT;

END;

blocked_managerこの制約を管理するためにテーブルは必要ありません。employeeこの情報は、テーブルから取得できます。

または、Oracle 11i より前のバージョンの場合:

CREATE OR REPLACE PACKAGE employees_trg
AS
  -- Table to hold identifiers of inserted/updated employee supervisors
  g_superssns sys.odcivarchar2list;
END employees_trg;

CREATE OR REPLACE TRIGGER employee_biu
  BEFORE INSERT OR UPDATE ON employee
IS
BEGIN
  -- Reset the internal employee supervisor table
  employees_trg.g_superssns := sys.odcivarchar2list();
END; 

CREATE OR REPLACE TRIGGER employee_aiur
  AFTER INSERT OR UPDATE ON employee
  FOR EACH ROW
IS
BEGIN
  -- Store the inserted/updated supervisors of employees
  IF (  (   INSERTING
        AND :new.superssn IS NOT NULL)
     OR (   UPDATING
        AND (  :new.superssn  <> :old.superssn
            OR :new.superssn IS NOT NULL AND :old.superssn IS NULL) ) )
  THEN           
    employees_trg.g_superssns.EXTEND;
    employees_trg.g_superssns(employees_trg.g_superssns.LAST) := :new.superssn;
  END IF;
END; 

CREATE OR REPLACE TRIGGER employee_aiu
  AFTER INSERT OR UPDATE ON employee
IS
DECLARE
  CURSOR csr_supervisors
  IS
    SELECT DISTINCT
           sup.column_value superssn
    FROM TABLE(employees_trg.g_superssns) sup
    ORDER BY sup.column_value;
  CURSOR csr_constraint_violations
    (p_superssn employee.superssn%TYPE)
  IS
    SELECT count(*) employees
    FROM employees
    WHERE pch.superssn = p_superssn
    HAVING count(*) > 5;
  r_constraint_violation csr_constraint_violations%ROWTYPE;
BEGIN
  -- Check if for any inserted/updated employee there exists more than
  -- 5 employees for the same supervisor. Serialise the constraint for each
  -- superssn so concurrent transactions do not affect each other
  FOR r_supervisor IN csr_supervisors LOOP
    request_lock('TOO_MANY_EMPLOYEES_' || r_supervisor.superssn);
    OPEN csr_constraint_violations(r_supervisor.superssn);
    FETCH csr_constraint_violations INTO r_constraint_violation;
    IF csr_constraint_violations%FOUND THEN
      CLOSE csr_constraint_violations;
      raise_application_error(-20001, 'Supervisor ' || r_supervisor.superssn || ' now has ' || r_constraint_violation.employees || ' employees');
    ELSE
      CLOSE csr_constraint_violations;
    END IF;
  END LOOP;
END;
于 2014-09-18T18:00:57.377 に答える