2012-04-06 72 views
0

我想創建一個觸發器,用PL/SQL更新另一個表,我遇到了一些問題。 (我讀過this,但沒有太大幫助)。Oracle PL/SQL觸發器更新另一列

這裏是我的情況,我可以說,2個表:

客戶

客戶ID號主鍵,ItemsDelivered

項目

客戶ID號,項ID號,ItemDelivered VARCHAR(15)

比方說,當somenone下訂單,我們在項目表看起來像這樣的新紀錄:

| CustomerID | ItemID | ItemDelivered | 
|   1 | 1 | False  | 

我想要一個觸發器,當有人更新ItemDelivered collum時將引發ItemsDelivered計數器n改爲「True」。

create or replace Trigger UpdateDelivered 
    After Update On Items For 
    Each Row 
Declare 
    Counter Customers.ItemsDelivered%Type; 
Begin 
    If (:Old.ItemDelivered ='False' And :New.ItemDelivered='True') Then 
    Select ItemsDelivered into Counter From Customers where CustomerdID =:New.CustomerID; 
    Update.... 
    end if; 
END; 

這是我的問題,如果只有ItemDelivered列更新沒有New.CustomerID!

有什麼辦法可以獲得剛剛更新的行的CustomerID? (我曾嘗試與插入虛擬表加入,但我得到的表不存在錯誤)上UPDATE

+2

Old.CustomerID是你在找什麼,我想。但你真的想這樣做嗎?只需加入並計數,當你需要計數。最終,你將開始嘗試維護像「ItemsDelivered」這樣的計算列的完整性問題。 – Glenn 2012-04-06 15:46:57

+1

@VGeorge:請詳細說明你想做什麼,爲什麼你想要:New.customerID?和我thnk你想要的:OLD.customerID? – 2012-04-06 15:47:05

+0

非常感謝您的幫助!舊參數像魅力一樣工作!我的情況與我所描述的有所不同,我簡化了一下。我想要做的事情實際上是提高一個人的工資,每當從「未婚」到「已婚」。感謝真的很多,我真的很感謝幫助:) – VGe0rge 2012-04-06 16:01:49

回答

3

在行級觸發器,既:new.customerID:old.customerID應該被定義。除非您更新CustomerID,否則兩者的價值相同。鑑於此,這聽起來像你想

create or replace Trigger UpdateDelivered 
    After Update On Items For 
    Each Row 
Begin 
    If (:Old.ItemDelivered ='False' And :New.ItemDelivered='True') Then 
    Update Customers 
     set itemsDelivered = itemsDelivered + 1 
     where customerID = :new.customerID; 
    end if; 
END; 

話雖這麼說,但是,存儲這種計數器和一個觸發維護它通常是設計一個數據模型有問題的途徑。它違反了基本的標準化,並且可能導致各種競爭條件。例如,如果您按最初顯示的方式對觸發器進行編碼,以獲取原始計數並進行更新,則會在多用戶環境中引入錯誤,因爲其他人也可能正在進行此過程標記交付的物品,並且這兩個交易都不會看到其他會話的更改,並且您的計數器將設置爲錯誤的值。即使你實現了無錯代碼,你也必須引入一個序列化機制(在這種情況下,UPDATECUSTOMERS表上的行級鎖),這會導致不同的會話必須相互等待, - 這會限制應用程序的可伸縮性和性能。

爲了證明:old.customerID:new.customerID都將被定義,都將是平等的

SQL> desc items 
Name          Null? Type 
----------------------------------------- -------- ---------------------------- 
CUSTOMERID           NUMBER 
ITEMID            NUMBER 
ITEMDELIVERED          VARCHAR2(10) 


SQL> ed 
Wrote file afiedt.buf 

    1 create or replace 
    2 trigger updateDelivered 
    3 after update on items 
    4 for each row 
    5 begin 
    6 if(:old.itemDelivered = 'False' and :new.itemDelivered = 'True') 
    7 then 
    8  dbms_output.put_line('New CustoerID = ' || :new.customerID); 
    9  dbms_output.put_line('Old CustomerID = ' || :old.customerID); 
10 end if; 
11* end; 
SQL>/

Trigger created. 

SQL> select * from items; 

CUSTOMERID  ITEMID ITEMDELIVE 
---------- ---------- ---------- 
     1   1 False 

SQL> update items 
    2  set itemDelivered = 'True' 
    3 where customerID = 1; 
New CustoerID = 1 
Old CustomerID = 1 

1 row updated. 
+0

非常感謝您的時間!我認爲新老將有相同的價值,但他們沒有!當我只更新ItemDelivered列時,new.customerID沒有任何價值!我測試過了,並且沒有工作!我同意你關於櫃檯的事情,我不會把它用於櫃檯,這是一個簡單的例子。祝你有個美好的時光:) – VGe0rge 2012-04-07 08:17:58

+0

@ VGe0rge - 我更新了我的答案,演示了':old.customerID'和':new.customerID'都將被定義,並且當你更新'itemDelivered'柱。 – 2012-04-07 17:04:33

1

如果你想存儲的項目數在數據庫中,我會建議對觸發器。您可以使用after row觸發器記錄項目編號(可能位於包中的表變量中)和after語句觸發器,它們將實際更新計數器,計算從基準日期直接傳送的項目。也就是說,通過

select sum(itemsDelivered) from Customers where itemId = :itemId; 

這樣,你避免損壞櫃檯的危險,因爲你總是將其設置爲它應該是什麼。將派生數據保存在單獨的表中可能是一個好主意。

我們完全基於數據庫觸發器構建了舊系統,這些數據庫觸發器更新了單獨的「派生」表中的數據,並且工作得非常好。它的優點是,我們所有的數據操作都可以通過插入,更新和刪除數據庫表來執行,而無需知道業務規則。例如,爲了讓一個學生進入一個班級,你只需要在註冊表中插入一行;在你選擇的聲明之後,學費,費用,財政援助和其他一切都已經計算好了。

+0

這是一個更好的解決方案,我必須說!當時我實際上不需要一個計數器,但我會在任何時候想要一個計數器。感謝您的時間:) – VGe0rge 2012-04-07 08:19:09

+0

請注意,您有這種方法的種族條件相同。如果有兩個會話將大致同時向兩個獨立客戶交付的相同物品進行標記,則兩個交易將不會彼此看到,您可以輕鬆地存儲與詳細信息不同的摘要信息。您可以使用快速刷新的物化視圖,刷新提交時存儲總是與細節數據同步的摘要信息。 – 2012-04-08 23:25:11

+0

其實沒有。關鍵是你總是從頭開始計算派生值,而不是從當前值開始計算。這樣他們總是與基礎數據保持一致。將派生數據保留在從未人爲更新的單獨表中也可以最大限度地減少鎖定問題。 – 2012-04-10 20:04:19