“如何修复 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') <> nvl(:new.trt_procedure, 'X') then
的内容检测。
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 中的触发器?的主要内容,如果未能解决你的问题,请参考以下文章