如何在 Oracle 数据库 10g 中编写 "IF UPDATING" 触发器?
How Can I code a "IF UPDATING" trigger in Oracle Database 10g?
我正在编写触发器以确保只能将一种货币设置为官方货币。我的意图是编写一个 "BEFORE INSERT OR UPDATE" 触发器。 INSERT 部分工作正常,但问题是编码 UPDATING 部分,因为当我尝试更新 table 时,我收到 ORA-04091 "mutanting table"。你有什么想法吗?
Table(只能设置一条记录为'Y'):
mon_id mon_description mon_official
----------------------------------------------
E EUR N
D DOL N
P PES Y
触发器:
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
你的代码有2个错误
第一
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
部分,更多关于变异错误的信息可以阅读这篇文章
第二个错误,你的逻辑不正确
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN
部分因为它可以是我们当前的行
试一试
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
IF :NEW.mon_oficial = 'Y' then
for m in (SELECT *
FROM monedas
WHERE mon_oficial = 'Y'
and rownum=1) loop
IF :NEW.mon_id <> m.mon_id THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
end loop;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
使用 AFTER INSERT OR UPDATE 语句触发器可以非常简单地完成此操作。相同的逻辑适用于这两种操作。
SELECT COUNT(*) INTO v_count FROM MONEDAS
其中 MON_OFICIAL = 'Y';
如果 v_count > 1 那么
RAISE_APPLICATION_ERROR...
这种方法的另一个优点:它允许语句
更新 MONEDAS SET MON_OFICIAL = CASE MON_ID WHEN 'A' THEN 'Y' ELSE 'N' END;
如果 MON_ID = 'A' 的行在先前的官方货币更新为 N 之前更新为 Y,则 row-level 触发器可能会引发错误时可以正常工作.
我认为这个问题最好用约束而不是触发器来解决。我不是这个答案的作者,但我认为它在这里是相关的。在其余的答案中,有一篇博客 post 的 link 建议避免触发,但 link 似乎不起作用。
答案在这里:
这是@tony-andrews 在回答中提供的示例:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);
我正在编写触发器以确保只能将一种货币设置为官方货币。我的意图是编写一个 "BEFORE INSERT OR UPDATE" 触发器。 INSERT 部分工作正常,但问题是编码 UPDATING 部分,因为当我尝试更新 table 时,我收到 ORA-04091 "mutanting table"。你有什么想法吗?
Table(只能设置一条记录为'Y'):
mon_id mon_description mon_official
----------------------------------------------
E EUR N
D DOL N
P PES Y
触发器:
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
你的代码有2个错误
第一
SELECT COUNT(:OLD.mon_oficial)
INTO v_count
FROM monedas
WHERE :OLD.mon_oficial = 'Y';
部分,更多关于变异错误的信息可以阅读这篇文章
第二个错误,你的逻辑不正确
IF v_count = 1 AND :NEW.mon_oficial = 'Y' THEN
部分因为它可以是我们当前的行
试一试
CREATE OR REPLACE TRIGGER mon_oficial_ins_trg
BEFORE
INSERT OR UPDATE
ON monedas
FOR EACH ROW
DECLARE
v_count NUMBER(8);
BEGIN
IF INSERTING THEN
SELECT COUNT(mon_oficial)
INTO v_count
FROM monedas
WHERE mon_oficial = 'Y';
IF v_count = 1 THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
IF UPDATING THEN
IF :NEW.mon_oficial = 'Y' then
for m in (SELECT *
FROM monedas
WHERE mon_oficial = 'Y'
and rownum=1) loop
IF :NEW.mon_id <> m.mon_id THEN
RAISE_APPLICATION_ERROR(
-20010, 'Only one record can be set as 'Y'');
END IF;
END IF;
end loop;
END IF;
END mon_oficial_ins_trg;
/
SHOW ERRORS;
使用 AFTER INSERT OR UPDATE 语句触发器可以非常简单地完成此操作。相同的逻辑适用于这两种操作。
SELECT COUNT(*) INTO v_count FROM MONEDAS 其中 MON_OFICIAL = 'Y';
如果 v_count > 1 那么 RAISE_APPLICATION_ERROR...
这种方法的另一个优点:它允许语句
更新 MONEDAS SET MON_OFICIAL = CASE MON_ID WHEN 'A' THEN 'Y' ELSE 'N' END;
如果 MON_ID = 'A' 的行在先前的官方货币更新为 N 之前更新为 Y,则 row-level 触发器可能会引发错误时可以正常工作.
我认为这个问题最好用约束而不是触发器来解决。我不是这个答案的作者,但我认为它在这里是相关的。在其余的答案中,有一篇博客 post 的 link 建议避免触发,但 link 似乎不起作用。
答案在这里:
这是@tony-andrews 在回答中提供的示例:
create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);