2017-04-21 77 views
0

我們使用視圖來抽象出我們的數據庫設計正在改變的事實......舊的,遺留代碼將所有用戶數據轉儲到一個非規格化表中;新設計在正常設計中將數據分開。出於多種原因(有些是合法的,有些是不合法的),我們的一些新開發必須針對新數據庫進行,因此用戶數據必須保持同步,直到新數據庫成爲用戶數據的權威來源。這是一個亂倫亂七八糟的事情,但這是我堅持的。由INSTEAD OF觸發器影響的更新()函數

我們選擇在視圖上使用替代觸發器來寫入兩個數據庫......唯一的另一種選擇是在權威源的表上寫入觸發器,但這可能會引入意外的副作用到現有的代碼中。

問題:如果我沒有在表中直接更新status_date,比如說,函數updating('status_date')在視圖上的任何觸發器中都返回false。但是,instead-of觸發器必須在其更新查詢中包含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表觸發回真正的 - 我要檢查的值,如果他們」重新相同,我必須假定日期沒有被設置。

-- 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

我有點困惑。通過表觸發器中的「不更新」檢查,兩個視圖更新都將日期設置爲今天。平等檢查既不更新它。但基本上你只是想要更新視圖,就像直接更新表一樣工作,無論你設置狀態日期爲自己(保持不變),設置爲特定日期還是不設置(因此它會SYSDATE)? –

回答

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 
相關問題