“如何修复 oracle pl/sql 中的触发器?

Posted

技术标签:

【中文标题】“如何修复 oracle pl/sql 中的触发器?【英文标题】:“How to fix trigger in oracle pl/sql? 【发布时间】:2019-07-28 00:59:17 【问题描述】:

问题: 需要表 A 的触发器,它执行以下操作: 每次在 TRT_PROCEDURE 列的表 A 中插入一个 INSERT 值,加 1 到表 B 中的列“TRT_INS_COUNT”。如果 TRT_PROCEDURE 值确实 B表中不存在,在B表中添加一行,用于程序设置 TRT_INS_COUNT 为 1。

每次对表 A 进行 DELETE 操作时,将表 A 中的 TRT_DEL_COUNT 列加 1 如果该过程值存在于表 B 中,则表 B 为该过程值。如果它不存在 存在于表 B 中,为过程在表 B 中添加一行并设置 TRT_DEL_COUNT 改为 1。

表 A 中的 TRT_PROCEDURE 列每次发生 UPDATE 时,将 1 添加到 表 B 中的 TRT_UPD_COUNT 列。如果不在表 B 中,则添加一行 表 B 的过程值并将 TRT_UPD_COUNT 设置为 1。如果 Column 表 A 中的 TRT_PROCEDURE 值已更改,将 TRT_UPD_COUNT 加 1 旧程序值。

通过执行多次 INSERT、DELETE 和 UPDATE 进行测试,然后显示表 B。

更新:每当将 trt_procedure 的值插入到表 B 中时,第二个相同的 trt_procedure(例如“88-20”)无法正确计入其指定的计数字段。不知道哪里出错了。

TABLE A
Name    Null     Type
TRT_ID  NOT NULL NUMBER(3)
PAT_NBR          NUMBER(4)
PHYS_ID          NUMBER(3)
TRT_PROCEDURE    VARCHAR2(5)
TRT_DATE         DATE


TABLE B
Name          Null     Type
TRT_PROCEDURE NOT NULL VARCHAR2(5)
TRT_INS_COUNT          NUMBER(3)
TRT_DEL_COUNT          NUMBER(3)
TRT_UPD_COUNT          NUMBER(3)

 TEST SAMPLES
  INSERT INTO A VALUES (11, 8031,101,'88-20',sysdate );
  INSERT INTO A VALUES (12, 5872,101,'60-00',sysdate );
  UPDATE A SET trt_procedure = '88-20' WHERE trt_id=6;
   /*row trt_id =6 cloumn trt_procedure old value '54-60'
    new value '88-20', which means Table B row '88-20' and
    row '54-60 both trt_upd_count should add 1*/

  DELETE FROM A WHERE trt_id=1; 
  /*row trt_id =1 cloumn trt_procedure value also '88-20', which means
    that Table B row '88-20' trt_del_count should also add 1*/

CREATE OR REPLACE TRIGGER trt_stats_trg
BEFORE INSERT OR UPDATE OR DELETE OF TRT_procedure ON A FOR EACH ROW
BEGIN
  IF INSERTING THEN
    UPDATE B SET trt_ins_count = trt_ins_count+1
    WHERE B.trt_procedure = :new.trt_procedure;
    IF SQL%NOTFOUND THEN
    INSERT INTO B (trt_procedure, trt_ins_count)
    VALUES (:new.trt_procedure, 1);
    END IF;

 ELSIF UPDATING THEN
   UPDATE B SET trt_upd_count = trt_upd_count+1
   WHERE B.trt_procedure = :old.trt_procedure;
   IF SQL%NOTFOUND THEN
   INSERT INTO B (trt_procedure, trt_upd_count)
   VALUES (:old.trt_procedure, 1);
   END IF;
   UPDATE B SET trt_upd_count = trt_upd_count+1
   WHERE B.trt_procedure = :new.trt_procedure;
   IF SQL%NOTFOUND THEN
   INSERT INTO B (trt_procedure, trt_upd_count)
   VALUES (:new.trt_procedure, 1);
   END IF;

 ELSIF DELETING THEN
   UPDATE B SET trt_del_count = trt_del_count+1
   WHERE B.trt_procedure = :old.trt_procedure;
   IF SQL%NOTFOUND THEN
   INSERT INTO B (trt_procedure, trt_del_count)
   VALUES (:old.trt_procedure, 1);
   END IF;
 END IF;
END trt_stats_trg;

【问题讨论】:

【参考方案1】:

这里有一些修复。

表格:

create table a(
  TRT_ID  NUMBER(3) NOT NULL ,
  PAT_NBR          NUMBER(4),
  PHYS_ID          NUMBER(3),
  TRT_PROCEDURE    VARCHAR2(5),
  TRT_DATE         DATE
);
create table b(
  TRT_PROCEDURE VARCHAR2(5) NOT NULL,
  TRT_INS_COUNT          NUMBER(3),
  TRT_DEL_COUNT          NUMBER(3),
  TRT_UPD_COUNT          NUMBER(3)
);

还有触发器

CREATE OR REPLACE TRIGGER trt_stats_trg
BEFORE INSERT OR UPDATE OR DELETE ON A FOR EACH ROW
BEGIN
  IF INSERTING THEN
    UPDATE B SET trt_ins_count = nvl(trt_ins_count,0)+1
      WHERE B.trt_procedure = :new.trt_procedure;
    IF SQL%NOTFOUND THEN
      INSERT INTO B (trt_procedure, trt_ins_count)
      VALUES (:new.trt_procedure, 1);
    END IF;

 ELSIF UPDATING('TRT_PROCEDURE') THEN
   if nvl(:old.trt_procedure, 'X') <> nvl(:new.trt_procedure, 'X') then
     UPDATE B SET trt_upd_count = nvl(trt_upd_count,0)+1
       WHERE B.trt_procedure = :old.trt_procedure;
     IF SQL%NOTFOUND THEN
       INSERT INTO B (trt_procedure, trt_upd_count)
       VALUES (:old.trt_procedure, 1);
     END IF;
     UPDATE B SET trt_upd_count = nvl(trt_upd_count,0)+1
       WHERE B.trt_procedure = :new.trt_procedure;
     IF SQL%NOTFOUND THEN
       INSERT INTO B (trt_procedure, trt_upd_count)
       VALUES (:new.trt_procedure, 1);
     END IF;
   end if;
 ELSIF DELETING THEN
   UPDATE B SET trt_del_count = nvl(trt_del_count, 0)+1
   WHERE B.trt_procedure = :old.trt_procedure;
   IF SQL%NOTFOUND THEN
     INSERT INTO B (trt_procedure, trt_del_count)
     VALUES (:old.trt_procedure, 1);
   END IF;
 END IF;
END trt_stats_trg;
/

关键点:

您可以使用UPDATING('COLUMN_NAME') 来检测SQL 何时影响特定列。

UPDATING 时,您可能希望检查未更改的更新,其中值为“ABC”的列正在更新为“ABC”。这通常发生在仅将所有列包含在更新中的框架中,即使它们并没有真正改变。使用类似于if nvl(:old.trt_procedure, 'X') &lt;&gt; nvl(:new.trt_procedure, 'X') then 的内容检测。

1234563当您将 1 添加到 NULL 时会发生这种情况。这可以通过为表中的列分配默认值0 或使用NVL(trt_upd_count,0) + 1 而不是trt_upd_count + 1 来解决。 最后,注意:new:old 的用法。看起来它们在此触发器中是正确的,但这取决于您的业务案例。

【讨论】:

【参考方案2】:

在您的触发器的IF INSERTING 部分中,您有

UPDATE B
  SET trt_ins_count = trt_ins_count+1
  WHERE :old.trt_procedure = :new.trt_procedure;

在插入新行时,:OLD 伪行中的所有值都是 NULL,因此您的 UPDATE 将永远不会更新任何内容。我想你的意思是

UPDATE B
  SET trt_ins_count = trt_ins_count+1
  WHERE B.trt_procedure = :new.trt_procedure;

尝试一下,看看这是否有助于解决您的问题。

【讨论】:

谢谢,IF UPDATING 部分是问题所在。 我这样编辑它,但它并没有正确计算更新的 TRT_PROCEDURE。 IF UPDATING THEN UPDATE B SET trt_upd_count = trt_upd_count+1 WHERE B.trt_procedure = :old.trt_procedure; IF SQL%NOTFOUND THEN INSERT INTO B (trt_procedure, trt_upd_count) VALUES (:old.trt_procedure, 1); END IF; UPDATE B SET trt_upd_count = trt_upd_count+1 WHERE B.trt_procedure = :new.trt_procedure; IF SQL%NOTFOUND THEN INSERT INTO B (trt_procedure, trt_upd_count) VALUES (:new.trt_procedure, 1); END IF; END IF; 每当表 A 中除 trt_procedure 之外的列被更新但 trt_procedure 未更新时,您的更新过程都会双倍计数。你增加 for :old.trt_procedure 和 for :new_trt_procedure 即使它们相同。 我按照建议修改了触发器。感谢您的提示,不幸的是问题仍然存在。修改后的代码如上。

以上是关于“如何修复 oracle pl/sql 中的触发器?的主要内容,如果未能解决你的问题,请参考以下文章