我正在尝试执行一个语句级触发器来强制执行以下“申请人一天内不能申请两个以上职位”的问题。
我能够使用行级触发器(如下所示)来执行它,但是当我不能使用:NEW或:OLD时,我不知道如何使用语句级触发器来执行它。
我知道有使用触发器的替代方法,但是我正在为考试进行修订,该考试也会有类似的问题,因此我将不胜感激。
CREATE TABLE APPLIES(
anumber NUMBER(6) NOT NULL, /* applicant number */
pnumber NUMBER(8) NOT NULL, /* position number */
appDate DATE NOT NULL, /* application date*/
CONSTRAINT APPLIES_pkey PRIMARY KEY(anumber, pnumber)
);
CREATE OR REPLACE TRIGGER app_trigger
BEFORE INSERT ON APPLIES
FOR EACH ROW
DECLARE
counter NUMBER;
BEGIN
SELECT COUNT(*) INTO counter
FROM APPLIES
WHERE anumber = :NEW.anumber
AND to_char(appDate, 'DD-MON-YYYY') = to_char(:NEW.appDate, 'DD-MON-YYYY');
IF counter = 2 THEN
RAISE_APPLICATION_ERROR(-20001, 'error msg');
END IF;
END;
答案 0 :(得分:1)
您的规则同时涉及多个行。因此,您不能使用FOR ROW LEVEL触发器:按照您的建议在APPLIES上进行查询会抛出ORA-04091:表正在更改异常。
因此,AFTER声明是这样。
CREATE OR REPLACE TRIGGER app_trigger
AFTER INSERT OR UPDATE ON APPLIES
DECLARE
cursor c_cnt is
SELECT 1 INTO counter
FROM APPLIES
group by anumber, trunc(appDate) having count(*) > 2;
dummy number;
BEGIN
open c_cnt;
fetch c_cnt in dummy;
if c_cnt%found then
close c_cnt;
RAISE_APPLICATION_ERROR(-20001, 'error msg');
end if;
close c_cnt;
END;
很显然,查询整个表在规模上效率很低。 (不建议使用触发器来进行此类操作的原因之一)。因此,在这种情况下,我们可能要使用a compound trigger(假设我们使用的是11g或更高版本)。
答案 1 :(得分:1)
您是正确的,因为您没有:OLD和:NEW值-因此您需要检查整个表以查看条件是否成立(我们不称其为“约束”,因为该术语在关系数据库的意义)已被破坏:
CREATE OR REPLACE TRIGGER APPLIES_AIU
AFTER INSERT OR UPDATE ON APPLIES
BEGIN
FOR aRow IN (SELECT ANUMBER,
TRUNC(APPDATE) AS APPDATE,
COUNT(*) AS APPLICATION_COUNT
FROM APPLIES
GROUP BY ANUMBER, TRUNC(APPDATE)
HAVING COUNT(*) > 2)
LOOP
-- If we get to here it means we have at least one user who has applied
-- for more than two jobs in a single day.
RAISE_APPLICATION_ERROR(-20002, 'Applicant ' || aRow.ANUMBER ||
' applied for ' || aRow.APPLICATION_COUNT ||
' jobs on ' ||
TO_CHAR(aRow.APPDATE, 'DD-MON-YYYY'));
END LOOP;
END APPLIES_AIU;
添加索引以支持此查询是一个好主意,这样它将有效地运行:
CREATE INDEX APPLIES_BIU_INDEX
ON APPLIES(ANUMBER, TRUNC(APPDATE));
好运。