2008年3月19日 星期三

斯斯有二種,Trigger也有二種喔(After/Before)

觸發程序(trigger)是用於在對於Table進行Insert、Update、Delete這三種行為時,會自動"被"執行的程序,所以我們可以在trigger裡寫一堆語法去檢查相關資料、連帶更新其它Table資料......。一般在 SQL SERVER 建立 trigger 時,如無特別定義的話預設是屬於 "After" 的trigger,也就是該Table資料已被異動,當然在trigger中可以對此異動做出復原動作;但如果所定義的是屬於"Before" 的trigger 時,該Table的資料尚未被異動,是否要異動是取決在trigger。
在trigger中SQL SERVER 提供了INSERTED 及 DELETED 這二個特殊暫存資料表格讓我們能更加靈活運用,下表為各異動行為中暫存資料表格裡所儲存的資料別:(若為"After" trigger 時,原資料表格皆未被異動)

異動行為/ 暫存資料表格

INSERTED

DELETED

原資料表格

INSERT(新增) 欲新增的"新"資料 無資料 為新增後的資料
DELETE(刪除) 無資料 欲被刪除的資料 為刪除後的資料
UPDATE(更新) 影響到資料的更新後資料 影響到資料的更新前資料 為更新後的資料

SQL SERVER 中一個trigger可以指定多重異動行為,所以在trigger中運用特殊暫存資料表格時需多加留意。

我們先建一個測試用資料表格和 Insert 的 Trigger。


程式15~29為建立一個dbo.MMX的After Insert Trigger,檢查欲塞入資料的A欄位值是否已存在dbo.MMX中。
程式32~45為建立一個dbo.MMX的Before Insert Trigger,功能和 dbo.tI_MMX 同樣;44行即當欲新增資料未存在於dbo.MMX時才把資料實際塞入dbo.MMX,如少了這步驟當 Insert Into .....跑完後dbo.MMX 裡並不會多一筆資料出來。




程式47~48:塞入一筆A欄位重覆的資料後發現跑完dbo.tII_MMX即中斷並未繼續執行dbo.tI_MMX,因為我們在42行下了return,所以有資料重覆情況時不會執行到44行程式,所以沒有資料被實際塞到dbo.MMX中,dbo.tI_MMX自然也不會被觸發起來。





程式50~51:塞入一筆未重覆的資料後,dbo.tII_MMX順利將資料INSERT INTO 到 dbo.MMX,連帶會觸發起dbo.tI_MMX的執行。


接下來我們來看看 UPDATE 的 Trigger。



程式53~69:建立一個After的Update Trigger,用來防止A欄位資料重覆。
程式71~95:建立一個Before的Update Trigger,同樣用來防止A欄位資料重覆。
程式75~76:如果一次UPDATE多筆資料時即中斷程式,也不會實際去異動dbo.MMX 裡的資料,為什麼要這麼做,等下會加以說明。
程式89~94:如果A欄位資料未重覆即更新dbo.MMX裡的資料。



為程式97~99執行完後的結果,因A欄位資料重覆所以在dbo.tUI_MMX即攔截掉,所以dbo.tU_MMX尚未被觸發執行。




程式 101~103執行後結果,因A欄位資料未重覆所以在dbo.tUI_MMX中會實際去異動dbo.MMX而觸發dbo.tU_MMX的執行。


Delete Trigger在這邊就省略不再加以說明,其寫法同INSERT、UPDATE Trigger的寫法,不過這邊要加以說明的是Before的INSTEAD OF UPDATE Trigger,相信眼尖的朋友應該已經看到INSTEAD OF UPDATE Trigger的致命點了吧,那即是不管是"Before"或是"After"的Trigger,在SQL SERVER上似乎是"批次"作業,就是如果是一次異動多筆資料其Trigger中的INSERTED及DELETED仍是多筆,而非執行多次的"單筆"Trigger,如果異動欄位又包含Primary Key 欄位時,在INSTEAD OF UPDATE Trigger 中將很難去下UPDATE 語法;在請教google大師後發現此問題好像仍無解,不知這問題有哪位朋友知道解決方式
,另外搜尋到ORACLE的Trigger 有 each row 語法可以下,不知是不是就是執行多次"單筆"Trigger的意思,也請熟悉ORACLE的朋友來賜教。

另外 INSTEAD OF Trigger 也可運用在 VIEW 中......,是的..,我們的確是可以對VIEW進行 INSERT、DELETE、UPDATE 等行為;




程式107~120:建立另一個dbo.MMX2,並將內容資料塞入。
程式122~128:建立一個VIEW,內容為 dbo.MMX 及 dbo.MMX2 JOIN出來的,注意程式126行,此VIEW是使用LEFT OUTER JOIN(外部聯結) 方式 JOIN出來的唷,所以dbo.MMX.A = 'C' 並無此筆資料,但透過外部聯結仍可呈現完整資料。




程式132~134:對dbo.VIEW_MMX 進行UPDATE行為時出現了4406的錯誤,不知是否因為用外部聯結而造成的???



程式136~164:建立INSTEAD OF UPDATE Trigger;
程式139為判斷可異動到Car_ID或Car_Name二欄位時即中斷程式;
程式141~162:建立一個Cursor 來逐筆對dbo.MMX進行實際異動;
程式149~156:因此VIEW是以外部聯結JOIN出來的,所以Car_Qty=0的資料有可能根本不存在於dbo.MMX中,所以我們需要一個判斷來決定我們對dbo.MMX是要進行INSERT或是UPDATE異動行為。



程式166~167:先將UPDATE前資料顯示出來。
程式169~170:針對VIEW下UPDATE語法(多筆批次更新)。
程式172~173:UPDATE後的資料。



異動前資料,dbo.MMX.A = 'C' 目前無此筆資料存在。



異動後資料,dbo.tUI_VIEW_MMX 為我們將 A in ('A','B','C') 等三筆資料的B欄位各加上2且INSERT一筆A='C'資料進來了 。
VIEW 的確是可被UPDATE,至於INSERT及DELETE相信也是可以辦得到的,如此看來INSTEAD OF Trigger 似乎較適合用在VIEW 的異動。


說到VIEW的異動,記得在軟體公司任職時,公司有請微軟講師來講解.NET開發工具,其中有提到VIEW可以做自動分割儲存,就是如果一個VIEW是來自數個不同Table,而這些Table有共同欄位,當異動資料時,SQL SERVER會依"設定"去異動實際Table,不過這功能名稱我忘了,改天再來測式一下囉。

SQL Script File:
http://cid-a640a79fdbb7450d.skydrive.live.com/embedrowdetail.aspx/BOLG%20Temp%20Files/Trigger%e6%b8%ac%e8%a9%a6Script.sql

1 則留言:

SamLiu 提到...

你在給我投綠的
就欠扁啦
為了我的股票
2號啦