AFTER插入触发器上的变异错误

时间:2018-05-13 13:08:00

标签: sql database oracle plsql

CREATE OR REPLACE TRIGGER TRG_INVOICE
    AFTER INSERT
    ON INVOICE
    FOR EACH ROW
DECLARE
    V_SERVICE_COST FLOAT;
    V_SPARE_PART_COST FLOAT;
    V_TOTAL_COST FLOAT;
    V_INVOICE_DATE DATE;
    V_DUEDATE DATE;
    V_REQ_ID INVOICE.SERVICE_REQ_ID%TYPE;
    V_INV_ID INVOICE.INVOICE_ID%TYPE;
BEGIN
    V_REQ_ID := :NEW.SERVICE_REQ_ID;
    V_INV_ID := :NEW.INVOICE_ID;

    SELECT SUM(S.SERVICE_COST) INTO V_SERVICE_COST
    FROM INVOICE I, SERVICE_REQUEST SR, SERVICE S, SERVICE_REQUEST_TYPE SRT
    WHERE I.SERVICE_REQ_ID = SR.SERVICE_REQ_ID
    AND SR.SERVICE_REQ_ID = SRT.SERVICE_REQ_ID
    AND SRT.SERVICE_ID = S.SERVICE_ID
    AND I.SERVICE_REQ_ID = V_REQ_ID;


    SELECT SUM(SP.PRICE) INTO V_SPARE_PART_COST
    FROM INVOICE I, SERVICE_REQUEST SR, SERVICE S, SERVICE_REQUEST_TYPE SRT, 
    SPARE_PART_SERVICE SRP,
     SPARE_PART SP
    WHERE I.SERVICE_REQ_ID = SR.SERVICE_REQ_ID
    AND SR.SERVICE_REQ_ID = SRT.SERVICE_REQ_ID
    AND SRT.SERVICE_ID = S.SERVICE_ID
    AND S.SERVICE_ID = SRP.SERVICE_ID
    AND SRP.SPARE_PART_ID = SP.SPARE_PART_ID
    AND I.SERVICE_REQ_ID = V_REQ_ID;


    V_TOTAL_COST := V_SERVICE_COST + V_SPARE_PART_COST;


    SELECT SYSDATE INTO V_INVOICE_DATE FROM DUAL;


    SELECT ADD_MONTHS(SYSDATE, 1) INTO V_DUEDATE FROM DUAL;

    UPDATE INVOICE
    SET COST_SERVICE_REQ = V_SERVICE_COST, COST_SPARE_PART = 
    V_SPARE_PART_COST,
   TOTAL_BALANCE = V_TOTAL_COST, PAYMENT_DUEDATE = V_DUEDATE, INVOICE_DATE = 
    V_INVOICE_DATE
    WHERE INVOICE_ID = V_INV_ID;



END;

我尝试在用户插入行后计算一些列。 使用service_request_id我想计算服务/部件/总成本。另外,我想生成创建和截止日期。但是,我一直在

  
    

INVOICE正在变异,触发/功能可能看不到它

  

在插入语句后不确定表是如何变异的。

1 个答案:

答案 0 :(得分:1)

  

在插入语句后不确定表是如何变异的。

想象一个简单的表格:

create table x(
  x int,
  my_sum int
);

和一个AFTER INSERT FOR EACH ROW触发器,与您的类似,它计算表中所有值的总和并更新my_sum列。

现在想象一下这个插入语句:

insert into x( x )
select 1 as x from dual
connect by level <= 1000;

此单个语句基本上会插入1000条记录,每条记录的值为1,请参阅此演示:http://sqlfiddle.com/#!4/0f211/7

因为在SQL中,每个单独的语句必须是ATOMIC(更多信息在这里:Statement-Level Read Consistency,只要最终结果正确(一致),Oracle就可以以任何方式自由执行此查询。它可以保存记录按执行顺序,可能按相反的顺序,它可以将批处理分成10个线程并并行执行。

由于触发器在插入每一行后单独触发,并且它无法事先知道“最终”结果,因此考虑到上述所有以下结果都是可能的,这取决于Oracle选择执行此查询的“内部”方法。如您所见,这些结果不符合一致性的定义。并且Oracle防止发出变异表错误。

换句话说 - 你的假设是坏的,你的设计有缺陷,你需要改变它。

| X | MY_SUM |
|---|--------|
| 1 |      1 |
| 1 |      2 |
| 1 |      3 |
| 1 |      4 |
...
...

或者也许:

| X | MY_SUM |
|---|--------|
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
| 1 |   1000 |
...

或者也许:

| X | MY_SUM |
|---|--------|
| 1 |      4 |
| 1 |      8 |
| 1 |     12 |
| 1 |     16 |
| 1 |     20 |
| 1 |     24 |
| 1 |     28 |
...
...