我们使用视图来抽象我们的数据库设计正在改变的事实......旧的遗留代码将所有用户数据转储到一个非规范化表中;新设计在正常设计中将数据分开。由于一些原因(一些是合法的,另一些不是),我们的一些新开发必须针对新数据库完成,因此用户数据必须保持同步,直到新数据库成为用户数据的权威来源。这是一个乱伦乱七八糟的混乱,但这是我坚持的。
我们选择在视图上使用替代触发器来写入两个数据库...唯一的另一个选择是在权威来源的表上写一个触发器,但这可能会引入意想不到的副作用现有的代码。
问题:如果我没有直接在表的更新中包含status_date,则函数updating('status_date')
在视图的任何触发器中返回false。但是,替代触发器必须在其更新查询中包含status_date字段,因此它似乎总是“设置”为相同的值。
例如,如果用户的状态发生更改,则当前用户表中的状态日期字段会自动更新(如果更新中未包含该字段):
-- Don't change the status date if a date has been explicitly set
if (not updating('status_date')) then
:new.status_date := sysdate;
end if;
如果我直接将表更新为:
update user_status set
status = 'GONE',
status_date = to_date('20170101','yyyymmdd')
where user_id = '123456';
该表将使用2017/01/01作为日期。但是,将日期保留为将触发sysdate。
-- The trigger will set status_date to sysdate
update user_status set
status = 'GONE'
where user_id = '123456';
在视图触发器中,我必须更新所有内容:
update user_status set
status = :new.status,
status_date = :new.status_date
where user_id = :new.user_id;
但是这导致updating('status_date')
总是在user_status表触发器中返回true - 我必须检查值,如果它们是相同的,我必须假设没有设置日期。
-- Don't change the status date if a date has been explicitly set
if (:new.status_date = :old.status_date) then
:new.status_date := sysdate;
end if;
在大多数情况下,它的工作方式相同。除非我通过明确设置来失去保持当前日期的能力 - 以下两个查询现在看起来相同,并且两者都将更新状态日期。但是,只有第二个应该:(
-- Not setting the date, trigger will use sysdate
update v_user_status set
status = 'GONE',
status_date = status_date
where user_id = '123456';
-- I'm correcting the status, but I need to keep the original date
update v_user_status set
status = 'GONE'
where user_id = '123456';
从视图触发器更新表时,如何保持字段的“未更新”状态?
答案 0 :(得分:0)
如果还原表触发器以使用not updating()
,则可以在视图触发器中使用相同的检查来决定使用哪个值,设置局部变量然后在表更新中使用它:
...
declare
l_status_date date;
begin
if (not updating('status_date')) then
l_status_date := sysdate;
else
l_status_date := :new.status_date;
end if;
update user_status set
status = :new.status,
status_date = l_status_date
where user_id = :new.user_id;
end;
/
表触发器总是将其视为更新;但它获得的相同列值与它本来可以获得或生成的相同。当你将它设置为自身时,在表触发器中使用相等检查不起作用,因为当你不想这样时,这仍然是正确的。
设置一些虚拟DDL和DML:
create table user_status (user_id varchar2(7), status varchar2(6), status_date date);
create trigger user_status_trig
before update on user_status
for each row
begin
-- Don't change the status date if a date has been explicitly set
if (not updating('status_date')) then
:new.status_date := sysdate;
end if;
end;
/
create view v_user_status as select * from user_status;
create trigger v_user_status_trig
instead of update on v_user_status
declare
l_status_date date;
begin
if (not updating('status_date')) then
l_status_date := sysdate;
else
l_status_date := :new.status_date;
end if;
update user_status set
status = :new.status,
status_date = l_status_date
where user_id = :new.user_id;
end;
/
insert into user_status values ('123456', 'NEW', date '2015-01-01');
commit;
直接使用三种方案更新表(每种方案之间的回滚):
update user_status set
status = 'GONE',
status_date = status_date
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2015-01-01
update user_status set
status = 'GONE',
status_date = to_date('20170101','yyyymmdd')
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2017-01-01
update user_status set
status = 'GONE'
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2017-04-21
通过视图重复相同的场景:
-- Setting the date to itself
update v_user_status set
status = 'GONE',
status_date = status_date
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2015-01-01
-- Setting the date, trigger will use that
update v_user_status set
status = 'GONE',
status_date = date '2017-01-01'
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2017-01-01
-- I'm correcting the status, but I need to keep the original date
update v_user_status set
status = 'GONE'
where user_id = '123456';
select * from user_status where user_id = '123456';
USER_ID STATUS STATUS_DAT
------- ------ ----------
123456 GONE 2017-04-21