更新/插入/删除表格中的某行后触发的触发器出现问题

时间:2019-05-06 13:25:18

标签: sql oracle plsql database-trigger

我有一个必须为作业创建的小型系统。这意味着可以更轻松地查看数据库中的相关数据以进行“管理”。

我有三个表:

  • CLIENT仅包含客户端ID,客户端名称,电话号码和电子邮件。

  • PROJECT很简单。它具有项目ID,引用CLIENT的客户端ID和项目名称。

  • PROJECT_PAYMENT包含项目付款ID,引用PROJECT的项目ID,然后包含具有付款到期日,已付款金额,未清金额等的行负载。

然后我有四个视图:

  • PAYMENTS_COMPLETED,您猜对了,它显示了已完成的付款。

  • PAYMENTS_OUTSTANDING与上述相反。

  • PAYMENTS_DISPUTED,显示客户或公司提出异议的所有付款。

  • PAYMENTS_PAST_DUE,其中显示尚未完成的付款以及付款到期日已过的地方。

然后我有一个更新所有这四个视图的过程:

create or replace PROCEDURE UPDATE_VIEWS AUTHID CURRENT_USER
IS

PAYMENTS_COMPLETED_STMNT VARCHAR2(5000);
PAYMENTS_DISPUTED_STMNT VARCHAR2(5000);
PAYMENTS_OUTSTANDING_STMNT VARCHAR2(5000);
PAYMENTS_PAST_DUE_STMNT VARCHAR2(5000);

BEGIN

    PAYMENTS_COMPLETED_STMNT := 'CREATE OR REPLACE VIEW PAYMENTS_COMPLETED AS SELECT PP.PROJECT_PAYMENT_ID, P.PROJECT_NAME, C.CLIENT_ID, C.CLIENT_NAME, PP.PAYMENT_DUE, PP.PAYMENT_TOTAL FROM PROJECT_PAYMENT PP JOIN PROJECT P ON PP.PROJECT_ID = P.PROJECT_ID JOIN CLIENT C ON C.CLIENT_ID = P.CLIENT_ID WHERE PP.PAYMENT_PAID >= PP.PAYMENT_TOTAL';
    PAYMENTS_DISPUTED_STMNT := 'CREATE OR REPLACE VIEW PAYMENTS_DISPUTED AS SELECT PP.PROJECT_PAYMENT_ID, P.PROJECT_NAME, C.CLIENT_ID, C.CLIENT_NAME, PP.PAYMENT_DUE, PP.PAYMENT_TOTAL, PP.PAYMENT_PAID, PP.PAYMENT_TOTAL-PP.PAYMENT_PAID AS "PAYMENT_REMAINING", PP.PAYMENT_DISPUTED_CLIENT, PP.PAYMENT_DISPUTED_COMPANY FROM PROJECT_PAYMENT PP JOIN PROJECT P ON PP.PROJECT_ID = P.PROJECT_ID JOIN CLIENT C ON C.CLIENT_ID = P.CLIENT_ID WHERE UPPER(PP.PAYMENT_DISPUTED_CLIENT) = ''Y'' OR UPPER(PP.PAYMENT_DISPUTED_COMPANY) = ''Y''';
    PAYMENTS_OUTSTANDING_STMNT := 'CREATE OR REPLACE VIEW PAYMENTS_OUTSTANDING AS SELECT PP.PROJECT_PAYMENT_ID, P.PROJECT_NAME, C.CLIENT_ID, C.CLIENT_NAME, PP.PAYMENT_DUE, PP.PAYMENT_TOTAL, PP.PAYMENT_PAID, PP.PAYMENT_TOTAL - PP.PAYMENT_PAID AS "PAYMENT_REMAINING" FROM PROJECT_PAYMENT PP JOIN PROJECT P ON PP.PROJECT_ID = P.PROJECT_ID JOIN CLIENT C ON C.CLIENT_ID = P.CLIENT_ID WHERE PP.PAYMENT_PAID < PP.PAYMENT_TOTAL';
    PAYMENTS_PAST_DUE_STMNT := 'CREATE OR REPLACE VIEW PAYMENTS_PAST_DUE AS SELECT PP.PROJECT_PAYMENT_ID, P.PROJECT_NAME, C.CLIENT_ID, C.CLIENT_NAME, PP.PAYMENT_DUE, PP.PAYMENT_TOTAL, PP.PAYMENT_PAID, PP.PAYMENT_TOTAL-PP.PAYMENT_PAID AS "PAYMENT_REMAINING" FROM PROJECT_PAYMENT PP JOIN PROJECT P ON PP.PROJECT_ID = P.PROJECT_ID JOIN CLIENT C ON C.CLIENT_ID = P.CLIENT_ID WHERE PP.PAYMENT_DUE < TRUNC(SYSDATE) AND PP.PAYMENT_PAID < PP.PAYMENT_TOTAL';

    EXECUTE IMMEDIATE PAYMENTS_COMPLETED_STMNT;
    DBMS_OUTPUT.PUT_LINE('UPDATED PAYMENTS_COMPLETED VIEW.');
    EXECUTE IMMEDIATE PAYMENTS_DISPUTED_STMNT;
    DBMS_OUTPUT.PUT_LINE('UPDATED PAYMENTS_DISPUTED VIEW.');
    EXECUTE IMMEDIATE PAYMENTS_OUTSTANDING_STMNT;
    DBMS_OUTPUT.PUT_LINE('UPDATED PAYMENTS_OUTSTANDING VIEW.');
    EXECUTE IMMEDIATE PAYMENTS_PAST_DUE_STMNT;
    DBMS_OUTPUT.PUT_LINE('UPDATED PAYMENTS_PAST_DUE VIEW.');

END;

现在进入我的问题。我创建了以下触发器:

create or replace TRIGGER UPDATE_VIEWS_ON_PP_INSERT_TG
AFTER INSERT OR UPDATE OR DELETE ON PROJECT_PAYMENT
BEGIN
    UPDATE_VIEWS();
    DBMS_OUTPUT.PUT_LINE('ALL VIEWS HAVE BEEN UPDATED.');
END;

我的意图是,只要有人在PROJECT_PAYMENT中插入新行,更新行或删除行,它就会触发。触发确实触发,但是它给了我以下错误,并阻止了我刚刚尝试插入的行:

One error saving changes to table "O015596H"."PROJECT_PAYMENT":
Row 11: ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "O015596H.UPDATE_VIEWS", line 16
ORA-06512: at "O015596H.UPDATE_VIEWS_ON_PP_INSERT_TG", line 2
ORA-04088: error during execution of trigger 'O015596H.UPDATE_VIEWS_ON_PP_INSERT_TG'
ORA-06512: at line 1

Local changes cleared

我不知道此错误的含义或解决方法,因此帮助这里的人可以告诉我问题是什么。我知道它说它不能提交触发器,但是我不知道我该怎么摆脱错误。

编辑1:

我在Google周围搜索,发现添加了以下内容:

FOR EACH ROW
DECLARE
    PRAGMA AUTONOMOUS_TRANSATION;

...可以在“ BEGIN”行上方使用,但是现在我遇到了“权限不足”错误:

One error saving changes to table "O015596H"."PROJECT_PAYMENT":
Row 11: ORA-01031: insufficient privileges
ORA-06512: at "O015596H.UPDATE_VIEWS", line 16
ORA-06512: at "O015596H.UPDATE_VIEWS_ON_PP_INSERT_TG", line 4
ORA-04088: error during execution of trigger 'O015596H.UPDATE_VIEWS_ON_PP_INSERT_TG'
ORA-06512: at line 1

1 个答案:

答案 0 :(得分:2)

视图是永久数据库结构。就像编写表或其他任何东西一样,只需编写DDL脚本即可。运行它们一次,然后将视图特权授予需要访问权限的人员。乔布斯是个好人。

除了任何其他内容,您都不想每次有人记录项目付款时都重新创建那些对象。获取对象锁将是一件令人头疼的事,并且由于会话状态无效,用户试图查询那些视图的尝试会不断失败。

要解释您遇到的错误:

  

ORA-04092:无法在触发器中提交

Oracle中的

DDL(例如CREATE VIEW语句)发出隐式提交。 Oracle不允许我们在触发器中包括COMMIT(或ROLLBACK),因为触发器作为事务的一部分触发,但是触发时不一定完成事务。

  

ORA-01031:特权不足

看起来您具有通过角色授予的CREATE VIEW特权。我们不能使用通过程序单位中的角色(存储过程,视图或触发器)间接授予的角色。


  

作业指定我必须证明如何使用过程,函数和触发器。

使用触发器创建视图并不能证明您知道“如何使用过程,函数和触发器” 。实际上相反。

触发器的一种更简单,更好的用法是使用amount paid来维护PROJECT_PAYMENT上的amount outstanding

create or replace TRIGGER UPDATE_VIEWS_ON_PP_INSERT_TG
before update ON PROJECT_PAYMENT for each row
BEGIN
    :new.amount_outstanding := :old.amount_outstanding - :new.amount_paid;
END;

这会减少amount outstanding的最新付款金额。 (我假设PROJECT_PAYMENT中的每条记录代表一次付款,而amount_paid不是滚动总额。)