0

我正在编写一个触发器,以确保只能将一种类型的钱设置为官方货币。我的意图是编写一个“在插入或更新之前”触发器。INSERT 部分工作正常,但问题是对 UPDATING 部分进行编码,因为当我尝试更新表时,我收到 ORA-04091“突变表”。你有什么主意吗?

表(只能设置一条记录为'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;
4

3 回答 3

1

在您的代码中有 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;
于 2017-06-30T05:24:50.013 回答
0

这可以通过 AFTER INSERT OR UPDATE 语句触发器非常简单地完成。相同的逻辑适用于这两种操作。

从 MONEDAS 中选择 COUNT(*) 到 v_count,其中 MON_OFICIAL = 'Y';

如果 v_count > 1 那么 RAISE_APPLICATION_ERROR...

这种方法的另一个优点:它允许语句

UPDATE MONEDAS SET MON_OFICIAL = CASE MON_ID WHEN 'A' THEN 'Y' ELSE 'N' END;

如果 MON_ID = 'A' 的行在之前的官方货币更新为 N 之前更新为 Y,则行级触发器可能会引发错误时正常工作。

于 2019-07-22T21:44:31.630 回答
0

我认为这个问题最好用约束而不是触发器来解决。我不是这个答案的作者,但我认为它在这里是相关的。在其余答案中,有一个指向建议避免触发器的博客文章的链接,但该链接似乎不起作用。

答案在这里: https ://stackoverflow.com/a/182427

这是@tony-andrews 在该答案中提供的示例:

create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);
于 2020-04-30T13:19:23.190 回答