是否有可能设计一个修改相同表(保证不相同行)的Oracle触发器?
我需要编写一个触发器,通过将旧值插入或更新到同一表中的另一行,在更新列之前保留该列的旧值。(是的,我知道)。 下面的合并/双重欺骗对我很有用, 但因为在这种情况下,我要插入或更新同一个表, Oracle在运行时抱怨。此外,出于某种原因,我发现编写编译时没有错误的代码异常困难 两个问题:是否有可能设计一个修改相同表(保证不相同行)的Oracle触发器?,oracle,plsql,triggers,Oracle,Plsql,Triggers,我需要编写一个触发器,通过将旧值插入或更新到同一表中的另一行,在更新列之前保留该列的旧值。(是的,我知道)。 下面的合并/双重欺骗对我很有用, 但因为在这种情况下,我要插入或更新同一个表, Oracle在运行时抱怨。此外,出于某种原因,我发现编写编译时没有错误的代码异常困难 两个问题: 即使我可以保证触发器永远不会更新触发触发器的行,也有可能修改触发器所在的同一个表吗?或者我必须做一些类似的事情(例如):将挂起的更改插入到另一个表中,以便第二个触发器可以将它们合并回原始表中?(此表是一个客户界面
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_属性正在变化,触发器/函数可能看不到它
ORA-06512:在第5行的“客户记住旧的状态”
您可以使用将旧值存储在每一行部分之前的变量中,然后在之后的语句
中合并
create or replace trigger remember_old_status
for update on event_properties
compound trigger
type t_type is table of event_properties%rowtype;
old_recs t_type := t_type();
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_recs.extend();
old_recs(old_recs.count).event_key := :old.event_key;
old_recs(old_recs.count).property_name := :old.property_name;
old_recs(old_recs.count).property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
forall i in old_recs.first..old_recs.last
merge into event_properties eprop
using (
select old_recs(i).event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_recs(i).property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_recs(i).property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end after statement;
end remember_old_status;
/
这假设您一次只更新一行:
create or replace trigger remember_old_status
for update on event_properties
compound trigger
old_rec event_properties%rowtype;
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_rec.event_key := :old.event_key;
old_rec.property_name := :old.property_name;
old_rec.property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
if (old_rec.property_name = 'CURRENT-STATUS') then
merge into event_properties eprop
using (
select old_rec.event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_rec.property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_rec.property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end if;
end after statement;
end remember_old_status;
/
快速测试:
insert into event_properties values('SOME_EVENT', 'CURRENT-STATUS', 'A');
1 row inserted.
update event_properties set property_val = 'B' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
1 row updated.
select * from event_properties;
EVENT_KEY PROPERTY_NAME PROPERTY_VAL
-------------------- -------------------- --------------------------------------------------------------------------------
SOME_EVENT CURRENT-STATUS B
SOME_EVENT PREVIOUS-STATUS A
update event_properties set property_val = 'C' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
1 row updated.
select * from event_properties;
EVENT_KEY PROPERTY_NAME PROPERTY_VAL
-------------------- -------------------- --------------------------------------------------------------------------------
SOME_EVENT CURRENT-STATUS C
SOME_EVENT PREVIOUS-STATUS B
如果要处理一条语句中的多个更新,则每行之前的可以填充一个集合,然后可以在之后的语句中使用该集合
create or replace trigger remember_old_status
for update on event_properties
compound trigger
type t_type is table of event_properties%rowtype;
old_recs t_type := t_type();
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_recs.extend();
old_recs(old_recs.count).event_key := :old.event_key;
old_recs(old_recs.count).property_name := :old.property_name;
old_recs(old_recs.count).property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
forall i in old_recs.first..old_recs.last
merge into event_properties eprop
using (
select old_recs(i).event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_recs(i).property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_recs(i).property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end after statement;
end remember_old_status;
/
您可以使用将旧值存储在每一行
部分之前的变量中,然后在之后的语句
中合并
create or replace trigger remember_old_status
for update on event_properties
compound trigger
type t_type is table of event_properties%rowtype;
old_recs t_type := t_type();
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_recs.extend();
old_recs(old_recs.count).event_key := :old.event_key;
old_recs(old_recs.count).property_name := :old.property_name;
old_recs(old_recs.count).property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
forall i in old_recs.first..old_recs.last
merge into event_properties eprop
using (
select old_recs(i).event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_recs(i).property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_recs(i).property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end after statement;
end remember_old_status;
/
这假设您一次只更新一行:
create or replace trigger remember_old_status
for update on event_properties
compound trigger
old_rec event_properties%rowtype;
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_rec.event_key := :old.event_key;
old_rec.property_name := :old.property_name;
old_rec.property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
if (old_rec.property_name = 'CURRENT-STATUS') then
merge into event_properties eprop
using (
select old_rec.event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_rec.property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_rec.property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end if;
end after statement;
end remember_old_status;
/
快速测试:
insert into event_properties values('SOME_EVENT', 'CURRENT-STATUS', 'A');
1 row inserted.
update event_properties set property_val = 'B' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
1 row updated.
select * from event_properties;
EVENT_KEY PROPERTY_NAME PROPERTY_VAL
-------------------- -------------------- --------------------------------------------------------------------------------
SOME_EVENT CURRENT-STATUS B
SOME_EVENT PREVIOUS-STATUS A
update event_properties set property_val = 'C' where event_key = 'SOME_EVENT' and property_name = 'CURRENT-STATUS';
1 row updated.
select * from event_properties;
EVENT_KEY PROPERTY_NAME PROPERTY_VAL
-------------------- -------------------- --------------------------------------------------------------------------------
SOME_EVENT CURRENT-STATUS C
SOME_EVENT PREVIOUS-STATUS B
如果要处理一条语句中的多个更新,则每行之前的可以填充一个集合,然后可以在之后的语句中使用该集合
create or replace trigger remember_old_status
for update on event_properties
compound trigger
type t_type is table of event_properties%rowtype;
old_recs t_type := t_type();
before each row is
begin
if (:old.property_name = 'CURRENT-STATUS') then
old_recs.extend();
old_recs(old_recs.count).event_key := :old.event_key;
old_recs(old_recs.count).property_name := :old.property_name;
old_recs(old_recs.count).property_val := :old.property_val;
end if;
end before each row;
after statement is
begin
forall i in old_recs.first..old_recs.last
merge into event_properties eprop
using (
select old_recs(i).event_key as event_key,
'PREVIOUS-STATUS' as property_name,
old_recs(i).property_val as property_val
from dual
) dummy
on (eprop.event_key = dummy.event_key
and eprop.property_name = dummy.property_name)
when matched then
update set property_val = old_recs(i).property_val
when not matched then
insert (event_key, property_name, property_val)
values (dummy.event_key, dummy.property_name, dummy.property_val);
end after statement;
end remember_old_status;
/
请看以下示例:。其中一个选项显示了如何使用“另一个表”(您提到的),这是一个全局临时表,而第二个选项演示了在包变量中使用集合。任何对Oracle文档的检查或在ORA-04091
上搜索此站点都会清楚地给出“否”的答案。不能对每行触发的表执行DML。解决这个问题的模式是对每一行使用更新另一个表。然后让一个after
语句级触发器使用另一个表中的信息来更新被触发的表。在问题的第二部分中,您指的是什么编译器错误?直接在代码中使用old.event\u键
可以编译OK。好吧,不管怎样,在一个问题中问两个问题是没有帮助的;如果您仍然可以重现实际代码和错误,那么您应该单独问一个问题。请看以下示例:。其中一个选项显示了如何使用“另一个表”(您提到的),这是一个全局临时表,而第二个选项演示了在包变量中使用集合。任何对Oracle文档的检查或在ORA-04091
上搜索此站点都会清楚地给出“否”的答案。不能对每行触发的表执行DML。解决这个问题的模式是对每一行使用更新另一个表。然后让一个after
语句级触发器使用另一个表中的信息来更新被触发的表。在问题的第二部分中,您指的是什么编译器错误?直接在代码中使用old.event\u键
可以编译OK。好吧,不管怎样,在一个问题中问两个问题是没有帮助的;如果您仍然可以重现代码和错误,那么您应该单独询问显示实际代码和错误的问题。