我遇到问题oracle触发器有问题,希望你们能在这里帮助我。
客户只能购买当前月份的5件物品,因此任何高于5的触发器都会引发异常。
这是我迄今为止所做的。
CREATE OR REPLACE TRIGGER five_reading
BEFORE INSERT OR UPDATE ON PURCHASE
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
totover NUMBER(3);
BEGIN
SELECT COUNT (custID)
INTO totover
FROM PURCHASE
WHERE :NEW.custID = PURCHASE.custID
AND EXTRACT(MONTH FROM SYSDATE)= EXTRACT(MONTH FROM :NEW.datevisited)
AND EXTRACT(YEAR FROM SYSDATE)= EXTRACT(YEAR FROM :NEW.datevisited);
IF totover = 5 THEN
RAISE_APPLICATION_ERROR ( -20001,
'Customer' || :NEW.custID ||
' already has 5 purchases' );
END IF;
END;
/
但是,当客户少于5次购买时,我仍然无法插入新记录。
答案 0 :(得分:0)
我很困惑 - 在你说的问题中,客户“...只能购买当前月份的5件物品,因此任何高于5的触发器都会抛出异常”,但触发器只会抛出异常客户已经完成了五次购买。
尝试以下方法:
CREATE OR REPLACE TRIGGER five_reading
BEFORE INSERT OR UPDATE ON PURCHASE
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
totover NUMBER;
BEGIN
SELECT COUNT(*)
INTO totover
FROM PURCHASE p
WHERE p.custid = :NEW.custid AND
TRUNC(p.datevisited, 'MONTH') = TRUNC(SYSDATE, 'MONTH');
IF totover > 5 THEN
COMMIT; -- autonomous transaction
RAISE_APPLICATION_ERROR ( -20001,
'Customer' || :NEW.custID ||
' already has ' || totover || ' purchases' );
END IF;
COMMIT; -- autonomous transaction
END;
此外,您没有说明在引发上述异常后会发生什么。捕获此异常时,您应该回滚外部事务。
分享并享受。
答案 1 :(得分: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 = 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_purchases
FOR INSERT OR UPDATE ON purchase
COMPOUND TRIGGER
-- Table to hold identifiers of inserted/updated customer purchases
g_custIDs sys.odcinumberlist;
BEFORE STATEMENT
IS
BEGIN
-- Reset the internal customer table
g_custIDs := g_custIDs();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
-- Store the inserted/updated customers
IF ( INSERTING
OR ( UPDATING
AND ( :new.custID <> :old.custID
OR ( trunc(:new.dateVisited) = trunc(sysdate, 'MM')
AND trunc(:old.dateVisited) <> trunc(sysdate, 'MM')))))
THEN
g_custIDs.EXTEND;
g_custIDs(g_custIDs.LAST) := :new.custID;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT
IS
CURSOR csr_customers
IS
SELECT DISTINCT
cst.column_value custID
FROM TABLE(g_custIDs) cid
ORDER BY cst.column_value;
CURSOR csr_constraint_violations
(p_custID purchase.custID%TYPE)
IS
SELECT count(*) purchases
FROM purchase pch
WHERE pch.custID = p_custID
AND trunc(pch.dateVisited, 'MM') = trunc(sysdate, 'MM')
HAVING count(*) > 5;
r_constraint_violation csr_constraint_violations%ROWTYPE;
BEGIN
-- Check if for any inserted/updated customer purchase there exists more than
-- 5 purchases for the same customer for the current month. Serialise the
-- constraint for each custID so concurrent transactions do not affect each
-- other
FOR r_customer IN csr_customers LOOP
request_lock('TOO_MANY_PURCHASES_' || r_customer.custID);
OPEN csr_constraint_violations(r_customer.custID);
FETCH csr_constraint_violations INTO r_constraint_violation;
IF csr_constraint_violations%FOUND THEN
CLOSE csr_constraint_violations;
raise_application_error(-20001, 'Customer ' || r_customer.custID || ' now has ' || r_constraint_violation.purchases || ' purchases this month');
ELSE
CLOSE csr_constraint_violations;
END IF;
END LOOP;
END AFTER STATEMENT;
END;