李忠憲 2004/12/10增修
這篇文章只是概論,所以應該當成程式設計的課外讀物來閱讀。
文章雖然想要有系統的來介紹 ASP 程式設計的每個環節,但是通常重點都很瑣碎,這就造成一個現象:本文在結構上是有組織的,但內容卻相當繁瑣。當然我了解這會使文章很難閱讀,但是這麼短的篇幅卻不得不如此。
初學者大概可以從文章中了解程式設計的大略過程,也可以獲知自己下一步該研究什麼,但或許有許多內容都不知所云。已經開始寫程式的人,會看到許多程式設計的觀念和重點,應該會更深入了解整個 WEB 應用程式的運作機制。有經驗的程式設計師,大概能夠體會這些耳提面命的重點,而有會心的一笑,雖然您不一定認同我的見解和看法。
對我來說,寫這篇文章是件苦差事,如果不是因為擔任研習講師,我是不願意去回憶這些痛苦往事的。寫程式的確是很痛苦的,更別說要用文字去描述了。但是,程式寫出來時卻很快樂,讓你明知道痛苦,卻想繼續下去......
ASP 雖然在語法上延續 Visual Basic 的格式,但是因為牽涉到網頁設計,因而與 VB 就有了區隔。當然如果您已經非常熟悉 VB 的話,對於撰寫 ASP 程式將會有不小的幫助,然而 ASP 尚需要其他方面的基礎,以下的列表就是學習 ASP 應有的基礎:
- Dynamic HTML
- Client Script
- ASP
- ODBC + SQL
- 資料庫分析
至於更進一步的應用,則必須學習更多新的課題,例如:ActiveX、Flash......等等。
這樣的學習曲線不能說是短,然而比較起其他的 Web 程式,例如:Perl、PHP......等,ASP 要算是易學的了。
一般為了講述方便,我們會把 Web 程式設計這件事,分為三個層次,這種分類當然是粗糙的,我們實際寫程式時會了解的比這個三層架構理論更細膩。為了在開始寫程式前,能夠真正了解自己是在做些什麼,我們還是要介紹一下三層架構。
所謂三層架構,在系統分析的意義上是指使用者介面、應用程式、資料庫三件事情。做這三件事情的主機,在網路架構上剛好是 Client、Server、Data Center。如果我們把前一小節所介紹的各種程式語言,放入到三層架構來思考,就能比較清楚的理解 Web 程式設計是怎麼一回事。
使用者介面
應用程式
資料庫
Java Script
<--Cookie ASP
<--ODBC--> SQL
^
|
V^
|
V^
|
VDynamic HTML URI------> ActiveX
Database 在使用者介面部分,當伺服端應用程式接受HTTP的瀏覽要求後,會將設計好的網頁介面傳送到客戶端執行,使用者透過滑鼠或鍵盤的操作呼叫 Java Script 執行,Java Script 會改變 Dynamic HTML 的設定,使得網頁外觀改變或重組表單設定值,然後網頁透過 URI 將表單資料傳給伺服端的應用程式處理,如果應用程式需要紀錄使用者的身分資料(或相依於身分的資料),則會透過 Cookie(Session) 來要求客戶端電腦加以儲存和傳送。
在應用程式部分,當 ASP 收到網頁傳回來的資料後,進行資料剖析,並採取適當的步驟處理,例如:引用 ActiveX 物件進行磁碟檔案管理或寄發郵件......等等,如果需要將資料入庫,則會透過 ODBC 與後端資料庫取得聯繫。
在資料庫部分,當收到 ODBC 傳來的 SQL 指令時,即進行指令剖析並執行,然後把執行結果快取在記憶體中,以方便應用程式進一步的讀取或處理,並且將處理的結果同步回寫到資料庫中。
以上所介紹的流程,就是 Web 程式設計的概要。這整個過程在程式設計規劃或系統分析時,必須要仔細地加以考量。
Dynamic HTML(DHTML) 是為了改良舊有 HTML 網頁互動設計不足的產物,DHTML 主要著力於將網頁物件化以方便 Script 的撰寫和應用,為了讓網頁的版面能更為生動,另一種次語言 CSS 也誕生了,透過 CSS 語法來改變網頁外觀的樣式,更使得 DHTML 所創造出來的網頁物件有了使用者自訂外觀的可能性。
所謂網頁物件,其實是引用了程式設計裡面所謂「物件導向」的觀念,將網頁上面的各個元件其參數、事件驅動模式和內建函式封裝為一體,使得網頁成為階層式物件的集合。對於已經熟悉物件導向程式設計的人來說,這種做法簡直就是把 DHTML 重新架構成一個大型的物件函式庫,使得網頁的設計等同於程式設計,而有了更大的設計自由和開發的可能。
由 Microsoft 所主導的 DHTML 沿用 VB 的命名習慣,將網頁物件的參數稱為「屬性」,內建函式稱為「方法」。從事網頁程式設計的第一步,就是要了解每個網頁物件有哪些屬性可以運用,透過哪個事件來觸發,使用何種方法來產生效果,這些複雜的內容的確需要一本字典在身邊備查。
前面提到過網頁已經變成了階層式物件的集合,這些物件誰是誰的上層,誰是誰的下層,如何指定一個物件,就成為物件運用上的基本知識,也就是說我們除了要了解單一物件的三要素「屬性、事件、方法」之外,還要了解每種物件在整體架構上所佔有的位置,這樣才有辦法隨心所欲來設計網頁介面。
設計網頁介面必須從使用者的角度出發,雖然網頁設計已經相形複雜化,變成是一種專業,但是使用者對於網頁介面的需求卻沒有改變,直覺和美觀仍然是標準,不要用設計應用程式的方法來設計介面,使用者並不想了解你的設計邏輯,選項越多越容易造成網路迷失,使用者害怕去按一個陌生的按鈕或連結,然後跑到另一頁網頁不知道如何回來;選項越少,畫面越單純,就越多人愛用!
Client 端瀏覽器所跑的程式早期是由 Netscape 公司開發的 Java Script 獨領風騷,Java Script 只是借用 Java 的名字,本身與 Java 並沒有關係。當 IE 取代 Netscape 成為市場主流之後,Java Script 也就停止發展了。雖然微軟自行開發了 VB Script 來取代 Java Script,可是大家都不喜歡用。畢竟當初玩網路的人大多是玩 C 出身的,所以討厭 Basic 語法的繁雜,因此採用 C 語法的 Java Script 雖然已停止開發,但是還是廣受歡迎,微軟也不得不被迫推出完全與 Java Script 相容的 JScript。我們今天所用的 Java Script 其實是 JScript,它是微軟參照 Java Script 重新撰寫程式碼的作品,為了尊重原創精神我們依然稱它是「Java Script」。
Java Script 語法雖然承襲自 C 語言,但是物件導向部分的語法卻與 C++ 有很大的差異,採用了 VB 的語法格式,所以從語言特性來觀察,它算是 C 的函式加上 VB 的物件的融合體。
由於 Netscape 與 IE 兩家主要的瀏覽器,對於網頁物件的定義非常分歧,所以撰寫 Java Script 最麻煩的地方,就是如何讓它跨平台使用。就算是不考慮 Netscape 用戶的存在,單祇就 IE 來撰寫 Java Script ,也必須留意不同版本之間的差異性。例如: IE 5.0 的網頁物件就比 IE 4.0 多很多,所以某些 5.0 的程式不能在 4.0 執行,幸運的是 4.0 的程式在 5.0 裡面執行並沒有問題。又例如:IE 5.5 在網頁物件 focus 的處理上與 5.0 不同,這導致部分 5.0 程式碼在 5.5 執行時失去作用。
Java Script 在網頁上展現效果時,常常會因為傳輸尚未完全而造成錯誤,導致網頁無法開啟。當傳輸進行到一半時,有時候 Java Script 尚未傳完,這個時候去操作網頁元件觸動某一個事件時,瀏覽器會發現該事件所驅動的程式不存在,而要求進行網頁偵錯。有時候剛好反過來,Java Script雖然已經傳好了,但是某些網頁元件尚未載入到瀏覽器上,這時候 Java Script 嘗試去改變該元件的屬性,就會造成 Java Script 執行錯誤!
這兩種錯誤其實是可以避免的,主要有下列規則:
- 把 Java Script 放在 <HEAD> 和 </HEAD> 之間,那麼當網頁準備要顯示在瀏覽器時,程式已經完全傳輸完畢了,這就可以避免第一類型的錯誤。
- 需要改變到網頁元件屬性的程式碼,必須包裝在函式裡面,然後去檢查網頁是否已經傳完,如果傳完就允許執行。這樣可以避免第二類型錯誤。
如何檢查網頁是否傳完呢?其實是設定一個變數(例如:ok),然後在 <BODY> 的 ONLOAD 事件中,改變變數的值,因為 ONLOAD 事件只有在瀏覽器備妥網頁文件時才會觸發,如此一來,我們只要檢查變數的值有沒有改變,便可以得知網頁是否已經備妥!
不要讓自己寫的 Java Script 出錯,可以說是 WEB 程式設計的基本功夫。不管程式寫得多好,假使在網路塞車時,經常遇到網頁打不開的情形,那麼參觀者就會漸漸流失了。
上網時瀏覽器會對伺服器提出傳輸要求,為了方便程式設計的應用,除了會傳輸所要求網頁的網址之外,也會附帶傳輸兩種參數,一種稱為 URI ,另一種稱為 cookie 。伺服器必須去剖悉這些參數,才能夠得知使用者的要求與命令,而去進行相對應的處理,並將處理結果以網頁型式傳回給使用者。
URI 傳值主要用途是協調瀏覽器與伺服器的設定,例如:語系、傳值編碼方式、版本功能訊息、帳號認證、IP位址......等等。由於這些資訊可以決定伺服器將傳遞何種 HTML 內容給瀏覽器,瀏覽器將以何種方式顯示伺服器送來的 HTML ,因此 URI 一般也被稱為 HTML 檔頭。
其實以程式設計的角度來看,我們比較關心的是,如何將程式執行時所需要的參數傳給伺服器,在這方面主要有兩種做法,一種稱為 Query-String 傳值,另一種方法是透過 Form 表單傳值。前者的運作方式是,瀏覽器將參數(須先以 URL-encoding 方式編碼)串接在 URL 網址後面形成一長字串,然後要求伺服器以 GET 方式處理 URI ,伺服器接獲要求會從 URI 萃取 URL 網址字串後半段的參數,放置在 Query-String 變數中,交給 Server Side Script 或 CGI 程式處理(須先以 URL-decoding 方式解碼後才能取用。如果是使用 ASP ,這部分工作會自動在呼叫程式前完成)。
URL 網址字串的格式為:http://站台網址:埠號/程式檔名/偽裝路徑檔名?參數1=值1&參數2=值2.......。通常埠號與偽裝路徑檔名會被省略,字串中的空白字元,必須改寫為 + 。值得一提的是如果有偽裝路徑檔名,那麼伺服器會通知瀏覽器把程式輸出的結果當成是該檔案的內容,這樣就可以將輸出內容當成一般檔案儲存或開啟執行。
Form 表單傳值則視場合而有不同,如果使用 GET 方式來傳遞,則做法與 Query-String 完全相同,另一種方式是使用 POST 方式來傳遞。因為 URI 參數每個欄位都有大小限制,無法用來傳送大量資料,因此當 Form 表單以 POST 方式傳值時,改將表單資料放置於鍵盤緩衝區,因此也就突破了容量限制。程式處理時,可以直接以讀取鍵盤輸入的方式讀取 Form 表單資料,如果是使用 ASP ,則會自動剖析參數並放置在陣列裡面供程式取用(ASP 缺乏處理上傳檔案資料的能力,須自行處理)。
了解了 URI 兩種傳值方式後,我們再來討論 cookie 傳值。cookie 是由 Netscape 公司發展出來的機制,現在已經成為瀏覽器的一項標準。cookie 比較正式的名稱是 session,顧名思義是用來紀錄每次連線的狀態。網路環境是多使用者環境,因此在程式設計上必須考慮不同身分使用者的需求,當使用者連上伺服器時,必須要有一個適當的方法來記錄身分資料。如果使用一般 URI 傳值來紀錄身分,會有兩個明顯的缺點,其一是資料不安全,容易被查閱,其二是資料必須完整的在伺服器與瀏覽器之間來回傳遞,耗用比較多的網路頻寬。使用 cookie 可以有效改善此種狀況。
一、安全性:cookie 內容不會顯示在瀏覽器的網址列或網頁內容,不容易追蹤。同時 cookie 只存放在個人的巡視設定檔內,所以不同帳號的使用者無法看到你的 cookie 內容。另外,cookie 可以設定過期時間,一旦過期系統會立刻移除,無法事後分析追蹤。以 ASP 所產生的 cookie 會自動以 MD5 編碼,這種編碼目前尚無法使用一般設備破譯。
二、頻寬節流:cookie 只有在第一次設定或變更內容時,才需要從伺服器傳送給瀏覽器。 cookie 設定好了以後,只會單方面由瀏覽器送到伺服器,而且傳輸不同 URL 時只傳送相對應的 cookie,不需要完整紀錄整個站台所有會用到的身分資料訊息。
cookie 已經是網頁程式設計上不可或缺的角色,因此在 ASP 發展初期就被納入為內建物件,在 ASP 裡使用比較正式的名稱 session(當然與原始的 cookie 略有不同)。
ASP 其實是一種內嵌式 CGI(共通閘道介面)。CGI 是一種由網頁伺服器驅動執行的應用程式,因為執行結果必須在斷線之前回傳給網頁伺服器處理,因此有執行時間上的限制(預設值為 90 秒)。因為這層限制的存在,迫使我們在設計程式時不得不將大型系統分解成許多小程式來撰寫,撰寫時的首要考量,就是執行效率要高,執行時期不可太長造成 timeout。
當伺服器收到瀏覽器的要求時,會自動進行 MIME 判別,從網頁或檔案的附加檔名去判斷處理方式,如果發現是 ASP 檔案,就會將此檔案交給 ASP 程式剖析工具來處理。ASP 程式剖析工具其實是一個 DLL 動態函式庫,它會將伺服器所收到的 URI 處理好儲存到陣列裡面(也就是 ASP 內建的 Request 物件),同時讀取程式碼進行解譯和執行,程式執行時如果發生錯誤,會自動中斷並且傳送錯誤訊息給伺服器,如果順利執行完畢,則傳送結果給伺服器(使用 ASP 內建的 Response 物件),並且等候伺服器的下一次呼叫。(其它像 PHP 或 JAVA Serverlet等也是以同樣方式執行,當然所呼叫的動態函式庫不一樣)
因為 ASP 是以 in-process 方式執行,所以我們說它是內嵌式的 CGI。所謂 in-process 是指所有執行中的 ASP 程式都使用同一個程序(process),共用同一個記憶體區塊來執行。ASP 動態函式庫一旦執行,就會常駐在記憶體內,一直到關機為止才結束,因此它的執行效能遠高於附加檔名是 exe 或 com 的應用程式,通常這一類應用程式的執行方式就稱為 out-process 。由於 in-process 的關係,所以 ASP 的內建物件 Application 才有辦法運作。但也是因為 in-process 的關係,如果想要變更 ASP 相關的系統設定,必須將伺服器重新開機才會生效。
事實上由於 ASP 本身的限制,有許多的應用領域無法使用 ASP 開發,而必須使用 VB 來設計,因為研發成本過高(VB 版權昂貴)並不普及。因此許多牽涉到系統整合的高階應用,仍以 Perl 程式設計為主流。
這方面的限制主要有兩個,首先是 ASP 不允許呼叫 out-process 物件或程式,因此 VB 所附的眾多 ActiveX 物件,仍無法使用於 ASP 程式設計,這就造成許多基本功能必須要重新開發。不能呼叫外部程式,也就意味著別人開發的現成 exe,無法應用在 ASP 裡面,所以想利用小工具產生縮圖,或壓縮解壓......等等,都只是痴心妄想。 微軟本身針對 ASP 所開發的物件模組,到目前為止仍嫌不足,再加上不能引用其它資源,ASP 能做的實在是很有限了(資料庫除外)。
其次是 ASP 無法透過 windows API 取得帳號授權,這使得 ASP 僅能以 IUSR_XXX 帳號來執行,許多伺服器管理工作是必須以 administrator 權限來執行的,所以 ASP 無法應用在伺服器管理工作上,例如:NT帳號管理、設定檔案目錄權限、修改系統登錄......等等。
ASP 作為 CGI 應有的機制,是透過六個內建物件來完成。除了前面提過的 Application、Session、Request、Response 之外,還有較不常使用的 Server 與 ObjectContext。這個六個物件是用來維護 ASP 動態函式庫與網頁伺服器間的傳遞和控制:
當Web Server開機跑起來後,首先執行的是 Application物件,它定義了啟動IIS後Web站台運作所需的整體變數,並且會把這些變數一直放在記憶體中,等待ASP程式存取,一直到IIS Shutdown才消失。
當我們打開瀏覽器並輸入網址,也就是對Web Server送出 Request 要求,Web Server會自動進行 MIME 判別,並採取Windows一貫慣用的方法,也就是從檔案的附加檔名去判斷要交給哪個 DLL 來執行或解譯。
如果發現要處理的檔案附檔名是 .ASP 類型的檔案,就將此檔案交給 ASP.DLL 這個動態連結函式庫處理,它會將先將 Web Server 所收到的 URI 處理好,並儲存到記憶體裡面, 我們在寫 ASP 程式時可以用 Request 物件將這些資料取出來使用,同時讀取程式碼進行解譯和執行。Request 變數的有效期間較短,只能維持到 ASP 程式執行完畢之前。
執行ASP程式時如果發生錯誤,就會進行錯誤處理,自動將程序中斷,並且傳送錯誤訊息給Web伺服器傳送給瀏覽器。如果能順利執行完畢,則將結果使用 Response 物件傳送給Web Server,然後再由 Web Server 將網頁傳給使用者的瀏覽器。Response 變數會在 buffer 停留很短的時間,當 buffer 功能未開啟時,則直接送回給參觀者,而不儲存在記憶體內。
ASP 動態函式庫一旦執行,就會常駐在記憶體內,一直到關機為止才結束(稱為 in-process),因此它的執行效能遠高於附加檔名是 exe 或 com 的應用程式(通常這一類應用程式的執行方式就稱為 out-process)。由於使用 in-process 方式處理,所以 ASP 的內建物件 Application 才有辦法運作。也正因為ASP屬於 in-process 的關係,所以如果想要變更 ASP 相關的系統設定,必須將伺服器重新開機才會生效。
Session 物件用來維護每個瀏覽器連線的資訊,當我們要讓ASP程式能夠根據不同身分執行不同功能的時候,就會使用 Session 變數。此變數的有效時間預設是20分鐘,但是有〝連選得連任〞的特性,也就是說當網頁重新整理(Reload)後,Session的有效時間也跟著延長。但最長也只能維持到瀏覽器關閉,或清除 cookie 為止。
與伺服器有關的相關設定,則是透過 Server 物件來與 Web Server 溝通,例如:呼叫自家的函式庫、修改 Timeout 設定、讀取主目錄的絕對路徑、使用 HTML 或 URL 編碼(Encode) 法來處理 Response 變數等。
ObjectContext 物件則是與 CGI 運作機制不相干的物件,它用來管理 MTS(Microsoft Transaction Server)。MTS 允許在執行程式前先紀錄伺服器狀態,如果有部分程式執行失敗,則可以退回到 先前的狀態,只有當所有相關程式跑完且執行成功後,才會真正登錄改變後的伺服器狀態,就像是我們為 ASP 程式設定了還原點一樣,可以避免程式執行失敗之後,要自行復原伺服器狀態的麻煩。
MTS 通常使用在有需要較高的安全性、和多個程式需要作〝同步〞的場合,一般我們是用不到的。由於 MTS 對於記憶體的需求較高,如果我們網站設計上沒有這個需要,卻故意去使用它,那麼將會白白浪費許多Web Server 的系統資源。
ASP 僅能以 IUSR_WebServer 這個帳號來執行,造成許多伺服器管理的工作必須以 administrator 權限來執行,所以 ASP 無法呼叫 WindowsAPI 來取得系統帳號和應用在伺服器管理工作上,例如:帳號管理、設定檔案目錄權限、修改系統登錄......等等。
ActiveX 物件是將想要達成特定目的的程式封存在一起,形成一個獨立自足的物件。我們只要在ASP程式中對系統提出建立該物件的需要( Server.CreatObject),就可以完成特定工作,不需要再去編寫程式碼。ActiveX 物件讓我們可以像堆積木一樣的方式來設計程式,使得程式設計越來越快,越來越容易。
微軟公司基於ASP受限較多,因此開發了一些給 ASP 專用的 ActiveX 物件,例如我們常見的 計數器、廣告輪撥器(Ad Rotator)、檔案系統物件(FSO)、資料庫物件(ADO)、SMTP元件...等等。
ASP 所編寫的程式,並無法編譯成 ActiveX 物件,這就讓許多程式設計師,無法將成果作品編譯後打包發行,如果想要分享就必須公開原始程式碼,再不然只能 把程式碼編密;前者會與作者的利益衝突,後者則需保留原始程式碼,每次修改後都還要再進行一次編密,超難維護。
ActiveX物件源自於 VB 物件導向程式設計的觀念,它是將達成特定目的的程式封存在一起,形成一個獨立自足的物件。我們只要要求系統建立該物件,就可以完成特定工作,不需要再去編寫程式碼。ActiveX 物件讓我們可以像堆積木一樣來設計程式,使得程式設計越來越快,越來越容易。
然而當 ActiveX 物件導入到 ASP 裡面來時,一部分 VB 舊有的 ActiveX 物件因為呼叫到 out-process 函式無法使用,剩下的部分也因為輸出入介面的差異不能使用,一切需要重頭累積。
過去以 VB 開發程式時,都是將結果輸出到視窗裡面,而 ASP 則需要將結果輸出到 Response 物件中,這個差異造成傳統 ActiveX 物件無法繼續使用。微軟公司因此開發了一些給 ASP 專用的 ActiveX 物件,例如:計數器、廣告迴旋(Ad Rotator)、檔案存取(FSO)、資料庫物件(ADO)、SMTP元件、全文搜尋......等等。
由於 ASP 內建物件只是建立了一個完整的 CGI 環境而已,真正要稱得上「應用」,則必須借重這些 ActiveX 物件。當您需要的功能微軟公司並未提供,而網路上也找不到現成的(或免費的) ActiveX 物件時,解決辦法就只有自己寫一個了!
幸運的是 ActiveX 物件可以用 VB 、 C 、 JAVA .........等等程式語言來生產,只要您熟悉其中任何一套語言,並且了解 CGI 的運作模式,自己開發 ActiveX 並不會太難。
不幸的是 ASP 所編寫的程式,並無法編譯成 ActiveX 物件,這就讓許多優秀的 WEB 程式設計師,無法將研究成果以很容易的方法發行,如果要分享就必須公開原始程式碼,再不然只能編密;前者很沒保障,後者則很難維護。
當我們開始開發資料庫應用程式時,不可避免的必須先熟悉 ADO 物件中的子物件 Connection,Connection 物件用來建立資料庫連結,是進行任何資料庫處理前的第一個步驟。其實建立資料庫連結這件事,早就已經有一套國際通用的標準規範,稱為 ODBC(Open DataBase Connection),所以 Connection 物件只是呼叫 ODBC DLL 前的一道準備程序。
Connection 物件最重要的任務,是把連結字串(Connect String)傳送給 ODBC 處理,ODBC 會剖析該字串,並且使用特殊的通訊協定(TCP/UDP 1433)來與資料庫溝通,因此連結字串必須包含建立連結時所需要的一切資訊,包括:資料庫類型、伺服器名稱、資料庫名稱、登入資訊(帳號、密碼、認證方式......等等)、資料型態設定(語言別、數字、貨幣設定......等等)。當連結建立成功之後,Connecting 物件就會把這些資訊保留在記憶體內,以便隨時維護連線不使它中斷。
ODBC 由於是國際標準因此所有的資料庫都有支援,也就是說透過 ODBC 你可以連結任何後端資料庫,不管是哪一種平台的資料庫。例如:我們想連結 mySQL,那麼只要安裝 myODBC 的函式庫到我們的電腦上面,就可以在 ASP 裡面以 Connection 物件來存取 mySQL 資料庫。
在 Windows 控制台內,還有一個 ODBC 連結精靈,可以為我們自動產生連結字串,假使我們不清楚某一類型資料庫(例如:MSSQL 2000)連結字串的語法,我們就可以使用精靈來自動產生。產生出來的連結字串,可以直接透過精靈來測試,如果測試結果可以成功建立連結,我們就可以把連結字串儲存到檔案裡面,這種檔案就稱為 DSN (Data Source Name),DSN 檔案的內容是純文字,我們可以用記事本開啟,以便擷取其中的連結字串。
如果我已經有一個設定好的 DSN 檔案,我也可以直接在呼叫 ODBC 時,指定 DSN 檔名給 Connection 物件,而不需要在寫一大串連結字串,因此 DSN 可以說是連結字串的另一種型式。通常我們為了程式移植方便,不去使用 DSN 這個簡便的方法,而是把 DSN 的檔案內容複製成連結字串,直接寫在 ASP 程式碼中。這樣做的好處是,當程式移植到另一台主機時,只要對連接字串進行小修改甚至是不需修改,就可以直接執行,而不需要開啟精靈一步一步進行設定。
由於一個完整的資料庫應用程式,係由多個 ASP 程式組成,每段程式進行資料庫存取時,都需透過 ODBC 建立資料庫連結,這會造成資料庫開開關關,同一程序反覆執行的後果,而使得資料庫存取的效能變差。因此比較好的做法是在資料庫連結建立後,將 Connection 物件保存在 Application 變數內,如此一來,資料庫連結將與站台永遠同在,執行效能當然也會變好。
在新版的 IIS 伺服器上,支援 Connection pool 功能,也就是說即使 ASP 程式將 Connection 物件關閉了,但是它並不會真正消失,而會保留在記憶體區塊( pool)裡面,等下次再開啟時,直接回存連結物件而不是重新建立連結,這樣就會讓初學者的 ASP 程式的表現比較專業!
SQL 是另一項資料庫上的國際標準。所有的大型資料庫都支援 SQL標準,但是他們也自行開發特殊的語法或用法,稱為延伸 SQL,使用延伸 SQL 有一些好處,例如:指令功能比較強大或執行效能比較高,但是與其他資料庫的相容性也較差。如果程式的可攜性是首要考量,那就應該使用標準 SQL;反過來說,如果執行效能是首要考量,那就多使用延伸 SQL。
SQL 指令通常是在程式之中一個一個單獨執行,我們可以很容易在指令與指令中間安插一些額外的資料處理。但也有時候需要一次執行許多 SQL 指令,通常執行的時間也比較久,這樣的工作就不適合寫在 ASP 程式裡面,因為透過網路傳輸指令然後再執行,效能一定不好,所以正確的做法是將整批 SQL 指令預先寫好存放在資料庫裡面,然後在適當的時機以單一 SQL 指令執行它,這些預先儲存的整批指令就稱為「預儲程序」。
在資料庫程式設計上,有的人主張使用 ADO 內建的 RecordSet 物件來進行資料操作(例如:新增、修改、刪除),但是這樣做會降低 ASP 程式的移植性,有些大型資料庫並不理會這種指令,而且這些指令也無法透過延伸 SQL 來增進效能。即使不去考量移植性的問題,當程式需要對資料進行大量修改或刪除時,使用 RecordSet 物件所提供的「方法」(函式)效能也比較差,這是因為這些方法一次只能處理一筆記錄,當有多筆紀錄需要處理時,必須使用迴圈才能達成目標。因此比較正規的 ASP 程式設計,都是採用 SQL 來進行資料操作。
SQL 語法相當具有彈性,可以將多重敘述組合成單一指令,因此可以在一道指令時間裡,執行非常繁複的計算,然而為了將來程式維護方便,應該儘量避免使用複雜的指令,特別是當需要同時對多個資料表進行資料合併時。在這種場合裡,我們會先將查詢的指令在資料庫軟體上面建立起來,這樣就可以在資料異動時順便推算查詢的結果,而不是事後再被動的由 ASP 來要求運算,前者在效能表現上當然會比較好。這種做法稱為建立「視界」(View)。
在做資料庫合併查詢時,有內部合併和外部合併兩種不同的方法。前者是使用 Join 指令,而後者則是依賴 Where 條件篩選,由於前者可以配合資料庫索引(Index)來加速,因此執行效能比較好。除非判斷資料庫的合併條件時,需要進行額外的數學計算,如果只是單純的欄位對應,我們建議一律使用內部合併。
以上所談的,就是初學 SQL 應該知道或注意的事項。
資料庫理論發展至今已經衍生成一門複雜的學說,我們當然無意在這裡深究它,因為作為一位程式設計師,資料庫將會是一直縈繞身邊的主題,總是需要花費很多的時間在上面,不是三言兩語可以解釋清楚的!不過我們還是得談一些實務上應注意的事項。
資料庫要能運作的順暢,必須要經過「正規化」的分析,同時為了程式執行所需要的效率,有時也得適度的「反正規化」,正規化與反正規化有一定的分析方法,在一般資料庫理論裡都會深入探討,程式設計師應該花時間去了解。許多從事 WEB 程式設計的人,因為沒有受過資料庫理論的訓練,常常會犯些小錯誤,而導致經常得人工去處理資料衝突的問題,譬如:資料不一致、重複資料或多餘資料......等問題,尤其有些資料表是直接由 Access 的 MDB 檔轉入,再轉入過程中會有許多設定遺失,如果沒有仔細檢查,日後在運作時很容易發生問題。
總之,資料表並非只要有欄位就可以了,一般用途的資料表都應該建立主索引(primary key),主索引必須為唯一鍵值,以避免索引鍵衝突。經常用來與其他資料表參照的欄位,也應該建立索引(index),但要注意很少用到的索引將會造成反效果,使得資料庫效能變差,因為維護索引同樣必須耗費系統資源。
資料表中每個欄位,都應該設定恰當的資料類型,一般常見的錯誤是將所有的欄位都設定成文字欄位,並且使用預設欄位大小。這是偷懶的方法,這樣做不但浪費硬碟空間,也因為存放空間疏鬆造成執行效能上的浪費,特別是進行資料搜尋時。
資料表與資料表會互相參照的,應該要建立關聯(relation),建立關聯的好處是資料庫系統會自動替我們產生維護資料異動的「預儲程序」,當某個資料表異動時會觸發「預儲程序」,把需要跟著異動的資料表一併異動處理,自然就會降低資料衝突的風險。
如果必須合併三個以上的資料表才能得到查詢結果,那就應該先將合併方法預先設定於系統中,建立一個「視界」,以方便日後開啟使用,避免在程式中使用冗長 SQL 語法,造成效能瓶頸。
資料表在規劃時,應該注意欄位數量與紀錄筆數之間的關係,欄位數量越大,資料處理時所花費的暫存記憶體越多(每一單筆記錄都需要大量記憶體來記錄欄位資料),而紀錄筆數越多,處理所需要的時間會越久,這兩者間必須取得平衡。例如:課表資料,我們如果將一張課表以單一一筆紀錄儲存,那麼每一節課都需要三個欄位(班級、教師、科目),總計約需 40 個欄位,資料筆數為教職員工數量加上班級數量,假設為 200 筆。如果改為一節課一筆記錄,那麼欄位將縮減為 4 欄,但是筆數卻暴增為 8000 筆。以效能來說當然前者較好,但是後者卻可以節省許多記憶體用量(對執行 ASP 的主機而言)。
事實上,大型的資料庫應用系統並不適合在 WEB 上執行,這就像冰山一樣;冰山有百分之九十的體積是在水面下,而大型的資料庫應用系統也應該如此。大部分吃重的計算工作應該在幕後執行,只有條件輸入和運算結果的查詢適合在 WEB 上進行。開發大型系統時,最忌諱將一切功能 WEB 化,以目前寬頻網路的發展來說,還不足以支撐此種運作模式。
最後,我們要談的是資料庫維護。資料庫除了定期維護以外,也應該設定 Log 警鈴。維護計劃至少需包含:資料索引的檢查重建、儲存空間的壓縮或擴充、資料備份等三件事。Log 警鈴則是在資料庫發生問題或即將發生問題時,用來通知維護人員,可以使用聲響通知、電子郵件通知,有些系統甚至還可以傳送簡訊或 BBCall 給維護人員。
資料庫維護做得越徹底,系統就越穩定,越經得起災難的考驗。但是,過多的維護工作,也一樣會損害到伺服器的效能。
如果本文解答了你的困惑,我就不會太後悔寫這些!
如果看完本文,讓你產生更多的疑惑,那麼這篇文章也算是成功了!請您將疑惑轉化為尋找解答的衝動,繼續努力。
如果一知半解也沒關係,既然是課外讀物,讀完也就值得獎勵了!
最後,如果本文有謬誤,請來信指正。至於疏漏,從頭到尾都是,請您別介意!