ASP 概論

李忠憲 2004/12/10增修

前言

這篇文章只是概論,所以應該當成程式設計的課外讀物來閱讀。

文章雖然想要有系統的來介紹 ASP 程式設計的每個環節,但是通常重點都很瑣碎,這就造成一個現象:本文在結構上是有組織的,但內容卻相當繁瑣。當然我了解這會使文章很難閱讀,但是這麼短的篇幅卻不得不如此。

初學者大概可以從文章中了解程式設計的大略過程,也可以獲知自己下一步該研究什麼,但或許有許多內容都不知所云。已經開始寫程式的人,會看到許多程式設計的觀念和重點,應該會更深入了解整個 WEB 應用程式的運作機制。有經驗的程式設計師,大概能夠體會這些耳提面命的重點,而有會心的一笑,雖然您不一定認同我的見解和看法。

對我來說,寫這篇文章是件苦差事,如果不是因為擔任研習講師,我是不願意去回憶這些痛苦往事的。寫程式的確是很痛苦的,更別說要用文字去描述了。但是,程式寫出來時卻很快樂,讓你明知道痛苦,卻想繼續下去......

一、學習 ASP 的曲線

ASP 雖然在語法上延續 Visual Basic 的格式,但是因為牽涉到網頁設計,因而與 VB 就有了區隔。當然如果您已經非常熟悉 VB 的話,對於撰寫 ASP 程式將會有不小的幫助,然而 ASP 尚需要其他方面的基礎,以下的列表就是學習 ASP 應有的基礎:

  1. Dynamic HTML
  2. Client Script
  3. ASP
  4. ODBC + SQL
  5. 資料庫分析

至於更進一步的應用,則必須學習更多新的課題,例如:ActiveX、Flash......等等。

這樣的學習曲線不能說是短,然而比較起其他的 Web 程式,例如:Perl、PHP......等,ASP 要算是易學的了。

二、Web 程式設計概念

一般為了講述方便,我們會把 Web 程式設計這件事,分為三個層次,這種分類當然是粗糙的,我們實際寫程式時會了解的比這個三層架構理論更細膩。為了在開始寫程式前,能夠真正了解自己是在做些什麼,我們還是要介紹一下三層架構。

所謂三層架構,在系統分析的意義上是指使用者介面、應用程式、資料庫三件事情。做這三件事情的主機,在網路架構上剛好是 Client、Server、Data Center。如果我們把前一小節所介紹的各種程式語言,放入到三層架構來思考,就能比較清楚的理解 Web 程式設計是怎麼一回事。

使用者介面

應用程式

資料庫

Java Script

<--Cookie

ASP

<--ODBC-->

SQL

^

V

^

V

^

V

Dynamic HTML URI------>

ActiveX

Database

在使用者介面部分,當伺服端應用程式接受HTTP的瀏覽要求後,會將設計好的網頁介面傳送到客戶端執行,使用者透過滑鼠或鍵盤的操作呼叫 Java Script 執行,Java Script 會改變 Dynamic HTML 的設定,使得網頁外觀改變或重組表單設定值,然後網頁透過 URI 將表單資料傳給伺服端的應用程式處理,如果應用程式需要紀錄使用者的身分資料(或相依於身分的資料),則會透過 Cookie(Session) 來要求客戶端電腦加以儲存和傳送。

在應用程式部分,當 ASP 收到網頁傳回來的資料後,進行資料剖析,並採取適當的步驟處理,例如:引用 ActiveX 物件進行磁碟檔案管理或寄發郵件......等等,如果需要將資料入庫,則會透過 ODBC 與後端資料庫取得聯繫。

在資料庫部分,當收到 ODBC 傳來的 SQL 指令時,即進行指令剖析並執行,然後把執行結果快取在記憶體中,以方便應用程式進一步的讀取或處理,並且將處理的結果同步回寫到資料庫中。

以上所介紹的流程,就是 Web 程式設計的概要。這整個過程在程式設計規劃或系統分析時,必須要仔細地加以考量。

三、認識 Dynamic HTML

Dynamic HTML(DHTML) 是為了改良舊有 HTML 網頁互動設計不足的產物,DHTML 主要著力於將網頁物件化以方便 Script 的撰寫和應用,為了讓網頁的版面能更為生動,另一種次語言 CSS 也誕生了,透過 CSS 語法來改變網頁外觀的樣式,更使得 DHTML 所創造出來的網頁物件有了使用者自訂外觀的可能性。

所謂網頁物件,其實是引用了程式設計裡面所謂「物件導向」的觀念,將網頁上面的各個元件其參數、事件驅動模式和內建函式封裝為一體,使得網頁成為階層式物件的集合。對於已經熟悉物件導向程式設計的人來說,這種做法簡直就是把 DHTML 重新架構成一個大型的物件函式庫,使得網頁的設計等同於程式設計,而有了更大的設計自由和開發的可能。

由 Microsoft 所主導的 DHTML 沿用 VB 的命名習慣,將網頁物件的參數稱為「屬性」,內建函式稱為「方法」。從事網頁程式設計的第一步,就是要了解每個網頁物件有哪些屬性可以運用,透過哪個事件來觸發,使用何種方法來產生效果,這些複雜的內容的確需要一本字典在身邊備查。

前面提到過網頁已經變成了階層式物件的集合,這些物件誰是誰的上層,誰是誰的下層,如何指定一個物件,就成為物件運用上的基本知識,也就是說我們除了要了解單一物件的三要素「屬性、事件、方法」之外,還要了解每種物件在整體架構上所佔有的位置,這樣才有辦法隨心所欲來設計網頁介面。

設計網頁介面必須從使用者的角度出發,雖然網頁設計已經相形複雜化,變成是一種專業,但是使用者對於網頁介面的需求卻沒有改變,直覺和美觀仍然是標準,不要用設計應用程式的方法來設計介面,使用者並不想了解你的設計邏輯,選項越多越容易造成網路迷失,使用者害怕去按一個陌生的按鈕或連結,然後跑到另一頁網頁不知道如何回來;選項越少,畫面越單純,就越多人愛用!

四、認識 Client Side Script

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 執行錯誤!

這兩種錯誤其實是可以避免的,主要有下列規則:

  1. 把 Java Script 放在 <HEAD> 和 </HEAD> 之間,那麼當網頁準備要顯示在瀏覽器時,程式已經完全傳輸完畢了,這就可以避免第一類型的錯誤。
  2. 需要改變到網頁元件屬性的程式碼,必須包裝在函式裡面,然後去檢查網頁是否已經傳完,如果傳完就允許執行。這樣可以避免第二類型錯誤。

如何檢查網頁是否傳完呢?其實是設定一個變數(例如:ok),然後在 <BODY> 的 ONLOAD 事件中,改變變數的值,因為 ONLOAD 事件只有在瀏覽器備妥網頁文件時才會觸發,如此一來,我們只要檢查變數的值有沒有改變,便可以得知網頁是否已經備妥!

不要讓自己寫的 Java Script 出錯,可以說是 WEB 程式設計的基本功夫。不管程式寫得多好,假使在網路塞車時,經常遇到網頁打不開的情形,那麼參觀者就會漸漸流失了。

五、URI 傳值和 cookie

上網時瀏覽器會對伺服器提出傳輸要求,為了方便程式設計的應用,除了會傳輸所要求網頁的網址之外,也會附帶傳輸兩種參數,一種稱為 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 的運作與限制

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 動態函式庫與網頁伺服器間的傳遞和控制:

  1. 當Web Server開機跑起來後,首先執行的是 Application物件,它定義了啟動IIS後Web站台運作所需的整體變數,並且會把這些變數一直放在記憶體中,等待ASP程式存取,一直到IIS Shutdown才消失。

  2. 當我們打開瀏覽器並輸入網址,也就是對Web Server送出 Request 要求,Web Server會自動進行 MIME 判別,並採取Windows一貫慣用的方法,也就是從檔案的附加檔名去判斷要交給哪個 DLL 來執行或解譯。

  3. 如果發現要處理的檔案附檔名是 .ASP 類型的檔案,就將此檔案交給 ASP.DLL 這個動態連結函式庫處理,它會將先將 Web Server 所收到的 URI 處理好,並儲存到記憶體裡面, 我們在寫 ASP 程式時可以用 Request 物件將這些資料取出來使用,同時讀取程式碼進行解譯和執行。Request 變數的有效期間較短,只能維持到 ASP 程式執行完畢之前。

  4. 執行ASP程式時如果發生錯誤,就會進行錯誤處理,自動將程序中斷,並且傳送錯誤訊息給Web伺服器傳送給瀏覽器。如果能順利執行完畢,則將結果使用 Response 物件傳送給Web Server,然後再由 Web Server 將網頁傳給使用者的瀏覽器。Response 變數會在 buffer 停留很短的時間,當 buffer 功能未開啟時,則直接送回給參觀者,而不儲存在記憶體內。

  5. ASP 動態函式庫一旦執行,就會常駐在記憶體內,一直到關機為止才結束(稱為 in-process),因此它的執行效能遠高於附加檔名是 exe 或 com 的應用程式(通常這一類應用程式的執行方式就稱為 out-process)。由於使用 in-process 方式處理,所以 ASP 的內建物件 Application 才有辦法運作。也正因為ASP屬於 in-process 的關係,所以如果想要變更 ASP 相關的系統設定,必須將伺服器重新開機才會生效。

  6. Session 物件用來維護每個瀏覽器連線的資訊,當我們要讓ASP程式能夠根據不同身分執行不同功能的時候,就會使用 Session 變數。此變數的有效時間預設是20分鐘,但是有〝連選得連任〞的特性,也就是說當網頁重新整理(Reload)後,Session的有效時間也跟著延長。但最長也只能維持到瀏覽器關閉,或清除 cookie 為止。

  7. 與伺服器有關的相關設定,則是透過 Server 物件來與 Web Server 溝通,例如:呼叫自家的函式庫、修改 Timeout 設定、讀取主目錄的絕對路徑、使用 HTML 或 URL 編碼(Encode) 法來處理 Response 變數等。

  8. ObjectContext 物件則是與 CGI 運作機制不相干的物件,它用來管理 MTS(Microsoft Transaction Server)。MTS 允許在執行程式前先紀錄伺服器狀態,如果有部分程式執行失敗,則可以退回到 先前的狀態,只有當所有相關程式跑完且執行成功後,才會真正登錄改變後的伺服器狀態,就像是我們為 ASP 程式設定了還原點一樣,可以避免程式執行失敗之後,要自行復原伺服器狀態的麻煩。

  9. MTS 通常使用在有需要較高的安全性、和多個程式需要作〝同步〞的場合,一般我們是用不到的。由於 MTS 對於記憶體的需求較高,如果我們網站設計上沒有這個需要,卻故意去使用它,那麼將會白白浪費許多Web Server 的系統資源。

  10. ASP 僅能以 IUSR_WebServer 這個帳號來執行,造成許多伺服器管理的工作必須以 administrator 權限來執行,所以 ASP 無法呼叫 WindowsAPI 來取得系統帳號和應用在伺服器管理工作上,例如:帳號管理、設定檔案目錄權限、修改系統登錄......等等。

  11. ActiveX 物件是將想要達成特定目的的程式封存在一起,形成一個獨立自足的物件。我們只要在ASP程式中對系統提出建立該物件的需要( Server.CreatObject),就可以完成特定工作,不需要再去編寫程式碼。ActiveX 物件讓我們可以像堆積木一樣的方式來設計程式,使得程式設計越來越快,越來越容易。

  12. 微軟公司基於ASP受限較多,因此開發了一些給 ASP 專用的 ActiveX 物件,例如我們常見的 計數器、廣告輪撥器(Ad Rotator)、檔案系統物件(FSO)、資料庫物件(ADO)、SMTP元件...等等。

  13. ASP 所編寫的程式,並無法編譯成 ActiveX 物件,這就讓許多程式設計師,無法將成果作品編譯後打包發行,如果想要分享就必須公開原始程式碼,再不然只能 把程式碼編密;前者會與作者的利益衝突,後者則需保留原始程式碼,每次修改後都還要再進行一次編密,超難維護。

七、使用 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 程式設計師,無法將研究成果以很容易的方法發行,如果要分享就必須公開原始程式碼,再不然只能編密;前者很沒保障,後者則很難維護。

八、ODBC 連結機制

當我們開始開發資料庫應用程式時,不可避免的必須先熟悉 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 指令通常是在程式之中一個一個單獨執行,我們可以很容易在指令與指令中間安插一些額外的資料處理。但也有時候需要一次執行許多 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 給維護人員。

資料庫維護做得越徹底,系統就越穩定,越經得起災難的考驗。但是,過多的維護工作,也一樣會損害到伺服器的效能。

後記

如果本文解答了你的困惑,我就不會太後悔寫這些!

如果看完本文,讓你產生更多的疑惑,那麼這篇文章也算是成功了!請您將疑惑轉化為尋找解答的衝動,繼續努力。

如果一知半解也沒關係,既然是課外讀物,讀完也就值得獎勵了!

最後,如果本文有謬誤,請來信指正。至於疏漏,從頭到尾都是,請您別介意!