【发布时间】:2018-07-26 15:38:25
【问题描述】:
我需要编写一个 TRIGGER,通过将旧值插入或更新到同一表中的另一行 来保留列的旧值。(是的,我知道)。 以下 MERGE/DUAL 诡计对我很有帮助, 但是因为在这种情况下,我要插入或更新同一个表, Oracle 在运行时抱怨。另外,由于某种原因,我发现编写编译时不会出错的代码异常困难。
两个问题:
- 是否可以修改触发器所在的同一个表,即使我可以保证触发器永远不会更新触发触发器的行?还是我必须做类似的事情(例如):将待处理的更改插入 another 表,以便第二个触发器可以将它们合并回 original 表? (此表是一个客户界面,因此我无法重新设计它以使用第二个表来永久存储旧值。)
- 不允许让我使用 :old.event_key, 但允许让我使用 的编译器错误是什么: old.property_val 在 MERGE 语句中? (声明一个变量 old_event_key 并将其分配给 :old.event_key 似乎 工作)是否有某种 隐藏中间语言 知道一列何时是主键(部分),并阻止您通过 :old.? 引用它
这是有问题的代码:
create or replace trigger remember_old_status
before update on event_properties
for each row
when (old.property_name = 'CURRENT-STATUS')
declare
old_event_key varchar2(20);
begin
old_event_key := :old.event_key;
merge into event_properties eprop
using (select 1 from dual) dummy
on ( eprop.event_key = old_event_key
AND eprop.property_name = 'PREVIOUS-STATUS')
when matched then
update set property_val = :old.property_val
when not matched then
insert (event_key, property_name, property_val)
values (old_event_key, 'PREVIOUS-STATUS', :old.property_val);
end;
这是表格:
CREATE TABLE "CUST"."EVENT_PROPERTIES"
( "EVENT_KEY" VARCHAR2(20 BYTE) CONSTRAINT "NN_FLE_FLK" NOT NULL ENABLE,
"PROPERTY_NAME" VARCHAR2(20 BYTE) CONSTRAINT "NN_FLE_PN" NOT NULL ENABLE,
"PROPERTY_VAL" VARCHAR2(80 BYTE),
CONSTRAINT "PX_EVENT_PROPERTIES" PRIMARY KEY ("EVENT_KEY", "PROPERTY_NAME") DEFERRABLE
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CUST_TS" ENABLE
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "CUST_TS" ;
以下是错误消息:
ORA-04091:表 CUST.EVENT_PROPERTIES 正在变异,触发器/函数可能看不到它 ORA-06512:在“CUST.REMEMBER_OLD_STATUS”,第 5 行
【问题讨论】:
-
看看这些例子:oracle-base.com/articles/9i/mutating-table-exceptions。其中一个展示了如何使用“另一个表”(您提到过)——这是一个全局临时表,而第二个选项展示了包变量中的集合的用法。
-
对 Oracle 文档的任何检查或在
ORA-04091上搜索此站点都会清楚地给您“否”的答案。您无法在触发for each row的表上执行 DML。解决这个问题的模式是使用for each row来更新另一个表。然后让after语句级触发器使用来自该其他表的信息来更新触发的表。 -
您在问题的第二部分中指的是哪些编译器错误?直接在代码中使用
old.event_key可以编译。好吧,无论如何,同时提出两个问题是没有帮助的。如果你仍然可以重现它,你应该问一个关于显示实际代码和错误的单独问题。