張未未,王蘋,杜清
觸發(fā)器(Trigger)是用戶定義在關(guān)系表上的一類由事件驅(qū)動的特殊過程。一旦定義,任何用戶對表的增、刪、改操作均由服務(wù)器自動激活相應(yīng)的觸發(fā)器,在DBMS核心層進(jìn)行集中的完整性控制[1]。觸發(fā)器類似于約束,但是比約束更加靈活,可以實(shí)施比FOREIGN KEY約束、CHECK約束更為復(fù)雜的檢查和操作,具有更精細(xì)和更強(qiáng)大的數(shù)據(jù)控制能力[2-3]。
Microsoft Access是一種小型的關(guān)系數(shù)據(jù)庫管理系統(tǒng),也被稱之為桌面型數(shù)據(jù)庫。作為Microsoft Office重要成員之一,由于 Access提供了基本數(shù)據(jù)庫管理功能和強(qiáng)大易用的應(yīng)用擴(kuò)展能力,而備受小型企事業(yè)數(shù)據(jù)管理者和數(shù)據(jù)庫初學(xué)者的青睞。但在實(shí)現(xiàn)數(shù)據(jù)完整性控制方面,Access所提供的更多的是聲明性數(shù)據(jù)完整性機(jī)制,例如:主鍵、默認(rèn)值、有效性規(guī)則、表間關(guān)系等,而一直以來并沒有提供在過程性數(shù)據(jù)完整性方面的擴(kuò)展,特別是對于觸發(fā)器機(jī)制的支持。但自O(shè)ffice 2010之后,Access數(shù)據(jù)庫中增加了一種新的宏類型—數(shù)據(jù)宏,用以實(shí)現(xiàn)類似觸發(fā)器的功能,從而極大地完善了Access數(shù)據(jù)庫的功能。
由于數(shù)據(jù)宏是由表事件觸發(fā)而執(zhí)行的,因此,也可以稱之為“事件驅(qū)動的數(shù)據(jù)宏”。數(shù)據(jù)宏允許在表事件(如插入、更新或刪除數(shù)據(jù)等)中運(yùn)行宏操作。每當(dāng)在表中添加、更新或刪除數(shù)據(jù)時,都會發(fā)生對應(yīng)的表事件,數(shù)據(jù)宏可以在發(fā)生這3種事件中的任何一種事件之后,或在發(fā)生刪除或更改事件之前運(yùn)行[4]。
Access中,數(shù)據(jù)宏按照被激活的時機(jī)不同,分為前期事件數(shù)據(jù)宏和后期事件數(shù)據(jù)宏,如圖1所示:
圖1 數(shù)據(jù)宏的分類
前期事件數(shù)據(jù)宏中宏操作在數(shù)據(jù)修改事件發(fā)生、但還未保存之前被觸發(fā)執(zhí)行,用以實(shí)現(xiàn)數(shù)據(jù)修改的完整性邏輯驗(yàn)證[5]。在前期事件數(shù)據(jù)宏中可以決定數(shù)據(jù)是否允許被真正修改,還是顯示錯誤以停止修改。
前期事件數(shù)據(jù)宏根據(jù)觸發(fā)的事件不同又可以分為更改(Update)前數(shù)據(jù)宏和刪除(Delete)前數(shù)據(jù)宏。
(1)更改前數(shù)據(jù)宏
更改前數(shù)據(jù)宏在記錄更改動作發(fā)生且保存記錄之前運(yùn)行,通常用來進(jìn)行邏輯驗(yàn)證,以決定記錄是否允許被修改或顯示錯誤以停止修改。
(2)刪除前數(shù)據(jù)宏
刪除前數(shù)據(jù)宏在記錄刪除動作發(fā)生且記錄被真正刪除之前運(yùn)行,通常用來進(jìn)行邏輯驗(yàn)證,以決定記錄是否允許被刪除或顯示錯誤以停止刪除。
后期事件數(shù)據(jù)宏中宏操作在數(shù)據(jù)修改事件發(fā)生,且修改已保存之后被觸發(fā)執(zhí)行,用以實(shí)現(xiàn)數(shù)據(jù)表中不同字段以及不同數(shù)據(jù)表間數(shù)據(jù)的連動更新[6]。
后期事件數(shù)據(jù)宏根據(jù)觸發(fā)的事件不同又可以分為插入(Insert)后數(shù)據(jù)宏、更新(Update)后數(shù)據(jù)宏和刪除(Delete)后數(shù)據(jù)宏。
(1)插入后數(shù)據(jù)宏
插入后數(shù)據(jù)宏是指在新記錄被添加到表后所運(yùn)行的邏輯。
(2)更新后數(shù)據(jù)宏
更新后數(shù)據(jù)宏是指在現(xiàn)有記錄被更改后所運(yùn)行的邏輯。
(3)刪除后數(shù)據(jù)宏
刪除后數(shù)據(jù)宏是指在記錄被刪除后所運(yùn)行的邏輯。
[舊]或[Old]記錄集:該記錄集用于臨時保存表中被更改或刪除的記錄在更改或刪除前的值??梢酝ㄟ^[舊].
[FieldName]來獲取不同字段的“舊”值。該對象通常用于更新后數(shù)據(jù)宏和刪除后數(shù)據(jù)宏[7]。
Updated(“Field Name”)函數(shù)用來判斷某個字段的值是否已更改。該函數(shù)通常用于更新后數(shù)據(jù)宏,可用于區(qū)分在不同字段值被更新后選擇執(zhí)行不同的宏操作。
如表1所示:
表1 數(shù)據(jù)宏常用操作
如圖2所示:
圖2 更新后數(shù)據(jù)宏舉例
當(dāng)將某記錄的“項(xiàng)目狀態(tài)”字段中的值設(shè)置為“未開始”時,則該記錄的“完成百分比”字段中的值會自動更改為0%;而“項(xiàng)目狀態(tài)”字段的值由“進(jìn)行中”改為“完成”時,則“完成百分比”字段中的值會自動更改為100%。該例所實(shí)現(xiàn)的效果是同一數(shù)據(jù)表中某一字段數(shù)據(jù)被更改后,其他字段數(shù)據(jù)的連動更新,因此,可以為“項(xiàng)目”數(shù)據(jù)表添加更新后數(shù)據(jù)宏。本例是微軟官方給出的數(shù)據(jù)宏應(yīng)用實(shí)例。
官方宏操作代碼如圖3(圖中加粗部分為數(shù)據(jù)宏各操作中的必須組成)所示:
圖3 更新后數(shù)據(jù)宏實(shí)例微軟官方代碼
但筆者執(zhí)行后并沒有發(fā)生預(yù)期的改變,而且在 Access的“應(yīng)用程序日志表”中會增加一條錯誤信息,提示“由于默認(rèn)別名表示的記錄處于只讀狀態(tài)”。經(jīng)過筆者反復(fù)試驗(yàn)發(fā)現(xiàn)問題出現(xiàn)在“EditRecord”操作的“別名”參數(shù)上,如圖4所示:
圖4 更新后數(shù)據(jù)宏對于被更改記錄的處理
當(dāng)在表中修改某一記錄的字段值時,系統(tǒng)會把當(dāng)前修改后的記錄映射為一個臨時的記錄集,并為此記錄集起一個“默認(rèn)別名”(“默認(rèn)別名”就是當(dāng)前修改記錄所在表的名字,對于本例來說“默認(rèn)別名”為“項(xiàng)目”),通過此別名,可以在數(shù)據(jù)宏中對于臨時記錄集進(jìn)行調(diào)用,從而得到該記錄修改后各字段的值。特別注意的是該臨時記錄集為只讀,而不能被修改。
而在數(shù)據(jù)宏的“EditRecord”操作中,由“別名”所指代的記錄集中的記錄將被修改。如果“別名”參數(shù)不寫,則表示使用“默認(rèn)別名”所代表的記錄集,而該記錄集是只讀的,因此,就會出現(xiàn)“由于默認(rèn)別名表示的記錄處于只讀狀態(tài)”的錯誤信息,從而導(dǎo)致數(shù)據(jù)宏“EditRecord”操作失敗。圖3所給出的微軟官方代碼是不能達(dá)到預(yù)期效果的。很多初次接觸數(shù)據(jù)宏的應(yīng)用開發(fā)人員會被這個例子所誤導(dǎo),而誤以為Access中的數(shù)據(jù)宏是不起作用的。
圖3所示代碼修改后如圖5所示:
圖5 更新后數(shù)據(jù)宏實(shí)例修改后代碼
修改后的代碼加入了“Updated("項(xiàng)目狀態(tài)")”函數(shù)用以判斷只有當(dāng)“項(xiàng)目狀態(tài)”這個字段被更新時才執(zhí)行后續(xù)宏操作,而其他字段更新不會執(zhí)行該宏,從而可以提高宏代碼效率。而“查找所選對象中的記錄”,即 LookupRecord操作是用來重新定位當(dāng)前被更改的記錄,在查找過程中使用了條件“[ID]=[舊].[ID]”。如前所述,“[舊]”記錄集保存了被更改的記錄在更改前的各字段的值,而對于“[ID]”字段實(shí)際是沒有進(jìn)行更改的,即被更改的記錄更新前后“[ID]”字段的值是一樣的。因此,可以據(jù)此條件在“項(xiàng)目”表中重新找到被更新的記錄,并形成名為“當(dāng)前更新記錄”的臨時記錄集。而這個記錄集是可讀寫的,因此,可以通過該記錄集完成對于表中其他字段值地修改。
Access數(shù)據(jù)庫中存在“tbl班級”和“tbl學(xué)生”兩張表,如圖6所示:
圖6 “tbl班級”表和“tbl學(xué)生”表
通過以“班級編號”字段為外鍵可以建立兩張表之間一對多的關(guān)系,從而限制“tbl學(xué)生”表中的班級編號的取值必須來自“tbl班級”表中已有的班級編號。但如果要使“tbl班級”表中“班長”字段中的學(xué)生編號必須是來自該班的學(xué)生的學(xué)生編號,即必須是本班的學(xué)生才有資格擔(dān)任本班的班長,這樣的表間數(shù)據(jù)約束是不能通過外鍵約束來實(shí)現(xiàn)的,因此可以考慮使用數(shù)據(jù)宏。由于當(dāng)在“班長”字段中輸入數(shù)據(jù),還未保存時就必須判斷是否符合要求,如果不符合要求將會顯示錯誤信息同時停止數(shù)據(jù)更改,所以為“tbl班級”表加入更改前數(shù)據(jù)宏[8]。
為“tbl班級”表添加“更改前數(shù)據(jù)宏”代碼如圖7所示:
圖7 更改前數(shù)據(jù)宏實(shí)例代碼
本例所實(shí)現(xiàn)的是過程性完整性約束,驗(yàn)證思路如圖 8所示:
圖8 更改前數(shù)據(jù)宏驗(yàn)證過程
通過“查找所選對象中的記錄”操作找到“新班長”學(xué)號在“tbl學(xué)生”表中的記錄,比較一下該記錄中的“班級編號”是不是與正在修改的班級的“班級編號”相同。如果不同說明“新班長”并不是當(dāng)前班的學(xué)生,沒有資格成為班長。這里注意,“tbl班級”是用默認(rèn)別名來表示修改后的數(shù)據(jù)所形成的記錄集。因此“[tbl班級].[班長]”可以得到修改后班長的學(xué)號。
使用“RaiseError”操作顯示錯誤信息,并可以撤銷對于字段數(shù)據(jù)的修改。操作后提示的錯誤信息如圖9所示:
圖9 更改前數(shù)據(jù)宏實(shí)例錯誤信息
通過以上實(shí)例不難發(fā)現(xiàn),對于數(shù)據(jù)宏的使用關(guān)鍵要把握住“[舊]”記錄集和“默認(rèn)別名”(以表名為默認(rèn)別名)記錄集的意義:
(1)“[舊]”記錄集用于臨時保存表中被更改或刪除的記錄在更改或刪除前的值。
(2)“默認(rèn)別名”記錄集用于臨時保存表中被更改的記錄在更改后的新值。
通過數(shù)據(jù)宏的使用可以在Access中實(shí)現(xiàn)類似于大型數(shù)據(jù)庫中觸發(fā)器的功能,從而實(shí)現(xiàn)過程性數(shù)據(jù)完整性定義和更加復(fù)雜的數(shù)據(jù)邏輯約束,可謂是 Access數(shù)據(jù)庫的一項(xiàng)重大改進(jìn)。在使用中應(yīng)注意特殊記錄集、函數(shù)和常用操作的功能,特別是數(shù)據(jù)修改時“別名”的使用,以達(dá)到正確書寫宏操作代碼的目的。
[1] 王珊,薩師煊.數(shù)據(jù)庫系統(tǒng)概論(第四版)[M].北京:高等教育出版社,2008.
[2] 嚴(yán)永慧.基于MS Office的企業(yè)項(xiàng)目管理系統(tǒng)的設(shè)計(jì)[J].微型電腦應(yīng)用,2013,29(4):12-15.
[3] 肖海蓉.觸發(fā)器技術(shù)在數(shù)據(jù)庫系統(tǒng)開發(fā)中的應(yīng)用研究[J].電腦開發(fā)與應(yīng)用,2011,24(7):36-38.
[4] 馬星光,劉仁權(quán).Access2010中醫(yī)藥數(shù)據(jù)庫實(shí)例教程[M].北京:中國中醫(yī)藥出版社,2012.
[5] 褚龍現(xiàn).DML觸發(fā)器保持?jǐn)?shù)據(jù)庫完整性應(yīng)用研究[J].計(jì)算機(jī)與現(xiàn)代化,2013(4):57-59.
[6] 胡鶴年.SQL Server觸發(fā)器在數(shù)據(jù)庫設(shè)計(jì)中的應(yīng)用[J].數(shù)據(jù)庫與信息管理,2012(8):37-38,83.
[7] http://msdn.microsoft.com/zh-cn/library/f?f973807(-v=office.14).aspx
[8] 汪星輝.基于 Access的教務(wù)管理系統(tǒng)的設(shè)計(jì)與應(yīng)用[J].計(jì)算機(jī)光盤軟件與應(yīng)用,2013(20):282-284.