[英]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.