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