簡體   English   中英

觸發器在Oracle SQL中不起作用

[英]Trigger doesn't working in oracle sql

我有一個名為Reading的表,還有一個列ReaderID用於指定誰讀取了該讀數。 現在,我想添加一個觸發器,以便在表Reading添加新行時,始終檢查閱讀器是否已閱讀5次。 如果是這樣,請勿進行插入。 為了確保一個讀者不能閱讀超過5次。 這是我的代碼,它不起作用,仍然可以插入該行而無需檢查。

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;

我不知道為什么

為了防止INSERT無法完成,你需要RAISE在觸發異常。 順便說一句,那些DBMS_OUTPUT.PUT_LINE()調用沒有任何意義-觸發器深入Oracle的內部,並且無處可顯示該​​輸出。

您需要替換以下行:

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;

按行:

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

在需要捕獲異常以避免在插入期間出錯后:

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

我假設您已經發現,您無法從定義了行級觸發器的表中進行選擇; 它會導致表變異異常。

為了使用觸發器正確地創建此驗證,應該創建一個過程來獲取用戶指定的鎖,以便可以在多用戶環境中正確地序列化驗證。

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;

然后可以在復合觸發器中使用此過程(假設至少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