执行约束的语句级触发器
Statement level trigger to enforce a constraint
我正在尝试实施语句级触发器来强制执行以下 "An applicant cannot apply for more than two positions in one day"。
我可以使用行级触发器强制执行它(如下所示),但是当我不能使用 :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;
您的规则同时涉及多行。所以你不能使用 FOR ROW LEVEL 触发器:按照你的建议查询 APPLIES 会抛出 ORA-04091: table is mutating exception.
所以,是 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;
显然,查询整个 table 在规模上是低效的。 (不推荐触发器用于此类事情的原因之一)。所以在这种情况下我们可能想要使用 a compound trigger(假设我们使用的是 11g 或更高版本)。
你是正确的,你没有 :OLD 和 :NEW 值 - 所以你需要检查整个 table 看看条件是否(我们不要称之为 "constraint" ,因为该术语在关系数据库的意义上具有特定含义)已被违反:
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));
祝你好运。
我正在尝试实施语句级触发器来强制执行以下 "An applicant cannot apply for more than two positions in one day"。
我可以使用行级触发器强制执行它(如下所示),但是当我不能使用 :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;
您的规则同时涉及多行。所以你不能使用 FOR ROW LEVEL 触发器:按照你的建议查询 APPLIES 会抛出 ORA-04091: table is mutating exception.
所以,是 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;
显然,查询整个 table 在规模上是低效的。 (不推荐触发器用于此类事情的原因之一)。所以在这种情况下我们可能想要使用 a compound trigger(假设我们使用的是 11g 或更高版本)。
你是正确的,你没有 :OLD 和 :NEW 值 - 所以你需要检查整个 table 看看条件是否(我们不要称之为 "constraint" ,因为该术语在关系数据库的意义上具有特定含义)已被违反:
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));
祝你好运。