我如何编码"如果更新"在Oracle数据库10g中触发?

时间:2017-06-30 02:11:00

标签: oracle plsql triggers dml mutating-table

我正在对触发器进行编码,以确保只能将一种类型的资金设置为官方资金。我的意图是代码a"在插入或更新之前"触发。 INSERT部分工作正常,但问题是编写更新部分,因为当我尝试更新表时,我收到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;

3 个答案:

答案 0 :(得分:0)

在您的代码中有2个错误

第一

SELECT COUNT(:OLD.mon_oficial)
        INTO   v_count
        FROM   monedas
        WHERE  :OLD.mon_oficial = 'Y'; 

部分,有关突变错误的更多信息,请阅读本文

enter link description here

第二个错误,

中的逻辑错误

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;

答案 1 :(得分:0)

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

从MONEDAS选择COUNT(*)到v_count 哪里MON_OFICIAL ='Y';

如果v_count> 1次    RAISE_APPLICATION_ERROR ...

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

UPDATE MONEDAS SET MON_OFICIAL = CASE MON_ID当'A'然后'Y'ELSE'N'END;

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

答案 2 :(得分:0)

我认为最好使用约束而不是触发器来解决此问题。我不是这个答案的作者,但我认为这很重要。在其余的答案中,有一个指向博客文章的链接,建议您避免使用触发器,但该链接似乎无效。

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

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

create unique index only_one_yes on mytable
(case when col='YES' then 'YES' end);