简体   繁体   English

触发器在Oracle SQL中不起作用

[英]Trigger doesn't working in oracle sql

I have a table called Reading , there is a column call ReaderID to specify the reading is read by who. 我有一个名为Reading的表,还有一个列ReaderID用于指定谁读取了该读数。 Now I want to add a trigger so when adding a new row in table Reading , always check if the reader has already read 5 times. 现在,我想添加一个触发器,以便在表Reading添加新行时,始终检查阅读器是否已阅读5次。 If so, don't do the insertion. 如果是这样,请勿进行插入。 To make sure one reader can not read more than 5 times. 为了确保一个读者不能阅读超过5次。 Here is my code,which is not working, the row can still be inserted without checking. 这是我的代码,它不起作用,仍然可以插入该行而无需检查。

CREATE OR REPLACE TRIGGER TRIGGER1 
BEFORE INSERT ON READING 
FOR EACH ROW
DECLARE
      varReaderCount   Int;
      varRID           Int;
BEGIN
    varRID := :new.ReaderID;

    SELECT COUNT(*) INTO varReaderCount
    FROM Reading
    WHERE ReaderID = varRID;

    IF (varReaderCount >= 5) THEN
      BEGIN
        DBMS_OUTPUT.PUT_LINE('*************************************************');
        DBMS_OUTPUT.PUT_LINE('The reader has reach the limitation of reading.');
        DBMS_OUTPUT.PUT_LINE('**************************************************');
      RETURN;
      END;
    END IF;
END;

I don't know why. 我不知道为什么

To prevent the INSERT from completing, you need to RAISE an exception in your trigger. 为了防止INSERT无法完成,你需要RAISE在触发异常。 And by the way, those DBMS_OUTPUT.PUT_LINE() calls don't make any sense -- the trigger runs deep in the bowels of Oracle and there's nowhere to display that output. 顺便说一句,那些DBMS_OUTPUT.PUT_LINE()调用没有任何意义-触发器深入Oracle的内部,并且无处可显示该​​输出。

You need to replace the lines : 您需要替换以下行:

IF (varReaderCount >= 5) THEN
    BEGIN
      DBMS_OUTPUT.PUT_LINE('*************************************************');
      DBMS_OUTPUT.PUT_LINE('The reader has reach the limitation of reading.');
      DBMS_OUTPUT.PUT_LINE('**************************************************');
      RETURN;
    END;
END IF;

by the lines : 按行:

IF (varReaderCount >= 5) THEN  
    RAISE_APPLICATION_ERROR(-20101,'limitation of reading');  
END IF;

After you need to catch the exception to avoid error during insert : 在需要捕获异常以避免在插入期间出错后:

 DECLARE
    readingException EXCEPTION;
    PRAGMA EXCEPTION_INIT(readingException, -20101);
 BEGIN 
    INSERT into READING VALUES (...);
 EXCEPTION
    WHEN readingException 
      THEN NULL;
 END;

As I assume you have discovered, you cannot select from the same table that a row-level trigger is defined against; 我假设您已经发现,您无法从定义了行级触发器的表中进行选择; it causes a table mutating exception. 它会导致表变异异常。

In order to properly create this validation using a trigger a procedure should be created to obtain user-specified locks so the validation can be correctly serialized in a multi-user environment. 为了使用触发器正确地创建此验证,应该创建一个过程来获取用户指定的锁,以便可以在多用户环境中正确地序列化验证。

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 = 1) THEN
    raise_application_error(-20001, 'dbms_lock.request Timeout');
  ELSIF (l_return = 2) THEN
    raise_application_error(-20001, 'dbms_lock.request Deadlock');
  ELSIF (l_return = 3) THEN
    raise_application_error(-20001, 'dbms_lock.request Parameter Error');
  ELSIF (l_return = 5) THEN
    raise_application_error(-20001, 'dbms_lock.request Illegal Lock Handle');
  ELSIF (l_return not in (0,4)) THEN
    raise_application_error(-20001, 'dbms_lock.request Unknown Return Value ' || l_return);
  END IF;
  -- Must COMMIT an autonomous transaction
  COMMIT;
END request_lock;

This procedure can then be used in a compound trigger (assuming at least Oracle 11, this will need to be split into individual triggers in earlier versions) 然后可以在复合触发器中使用此过程(假设至少Oracle 11,在早期版本中需要将其拆分为单独的触发器)

CREATE OR REPLACE TRIGGER too_many_readings
  FOR INSERT OR UPDATE ON reading
  COMPOUND TRIGGER

  -- Table to hold identifiers of inserted/updated readers
  g_readerIDs sys.odcinumberlist;

BEFORE STATEMENT 
IS
BEGIN
-- Reset the internal reader table
 g_readerIDs := g_readerIDs();
END BEFORE STATEMENT; 

AFTER EACH ROW
IS
BEGIN
  -- Store the inserted/updated readers
  IF (  INSERTING
     OR (   UPDATING
        AND :new.readerID <> :old.readerID))
  THEN           
    g_readerIDs.EXTEND;
    g_readerIDs(g_readerIDs.LAST) := :new.readerID;
  END IF;
END AFTER EACH ROW;

AFTER STATEMENT
IS
  CURSOR csr_readers
  IS
    SELECT DISTINCT
           rid.column_value readerID
    FROM TABLE(g_readerIDs) rid
    ORDER BY rid.column_value;
  CURSOR csr_constraint_violations
    (p_readerID reading.readerID%TYPE)
  IS
    SELECT count(*) readings
    FROM reading rdg
    WHERE rdg.readerID = p_readerID
    HAVING count(*) > 5;
  r_constraint_violation csr_constraint_violations%ROWTYPE;
BEGIN
  -- Check if for any inserted/updated reading there exists more than 5 readings
  -- for the same reader. Serialise the constraint for each readerID so
  -- concurrent transactions do not affect each other
  FOR r_reader IN csr_readers LOOP
    request_lock('TOO_MANY_READINGS_' || r_reader.readerID);
    OPEN csr_constraint_violations(r_reader.readerID);
    FETCH csr_constraint_violations INTO r_constraint_violation;
    IF csr_constraint_violations%FOUND THEN
      CLOSE csr_constraint_violations;
      raise_application_error(-20001, 'Reader ' || r_reader.readerID || ' has ' || r_constraint_violation.readings || ' readings');
    ELSE
      CLOSE csr_constraint_violations;
    END IF;
  END LOOP;
END AFTER STATEMENT;

END;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM