想想我們的項目里能不能用存儲過程。
不能:因為項目要涉及到不同的數(shù)據(jù)庫,更多的是為了以后換庫方便。在代碼里直接寫SQL語句進行查詢。發(fā)生換庫時,只要簡單的換一下連接字符串就可以了。這叫以不變應(yīng)萬變,怎么講呢,代碼不變,數(shù)據(jù)庫愛怎么變就怎么變。
換庫可以靈活到什么程度,即使公司打算把Oracle項目換成Access做單機版也沒有任何問題。
能:存儲過程的執(zhí)行速度要快得多,一方面它不需要在業(yè)務(wù)服務(wù)器和數(shù)據(jù)庫服務(wù)器之間折返,更因為它有一次編譯,較投遞 SQL語句而言,效率更高。況且Oracle可以用Java,C 編寫更高效的存儲過程,SQL Server 2005則可以使用 .net,毫無疑問,存儲過程的速度比美名其曰的業(yè)務(wù)組件要快的多。
一般人喜歡折衷。行嘛,既然二者都有理,我在適當(dāng)?shù)膱龊线x擇適當(dāng)?shù)募夹g(shù),需要效率高的時候,我才用存儲過程。
我喜歡一邊倒。既然存儲過程效率高,為什么不用存儲過程?
為了實現(xiàn)一個很小的業(yè)務(wù)運算,需要把數(shù)據(jù)提過來,然后又投回去,需要把數(shù)據(jù)庫的數(shù)據(jù)類型轉(zhuǎn)換成程序語言的數(shù)據(jù)類型,做完這些無謂的工作之后,最后的效果卻是拋回到數(shù)據(jù)庫,只要想到這一點,我們?yōu)槭裁床皇褂么鎯^程而采用這種低效的方案。
須知現(xiàn)在的問題背景和那個數(shù)據(jù)庫山頭林立的年代已經(jīng)完全不同了。如果說數(shù)據(jù)庫也存在階段的話,那種能否支持存儲過程都參差不齊的年代屬于石器時代。目前的數(shù)據(jù)庫,連MySQL都要支援存儲過程了,也就是說,它們基本上都在同一條起跑線。因此,無須考慮萬一我寫了存儲過程,而“升遷”的目標(biāo)數(shù)據(jù)庫不支援存儲過程的問題。
但是,各種數(shù)據(jù)庫的存儲過程的編寫方法并不一致。如果從SQLServer轉(zhuǎn)移到Oracle,意味著幾乎所有的存儲過程都要重寫。這是換庫說在新時代的新說辭。這個說法是有事實憑據(jù)的,換庫多麻煩,存儲過程要全部重寫。
實際上這個問題很容易解決——代碼生成。做好一個存儲過程的模板,將模板中的數(shù)據(jù)庫類型標(biāo)識換成"Oracle",重新生成一次便萬事大吉了。既然不需要手工重寫,這個說法自然也站不住腳?;蛘咧谱鲆环N調(diào)和于主流數(shù)據(jù)庫存儲過程語言的中間語言也可以,一個簡單的編譯器將它轉(zhuǎn)換為特定于某個數(shù)據(jù)庫的語言。
使用存儲過程除了效率高之外還有其他優(yōu)勢:
a) 業(yè)務(wù)規(guī)則腳本化。編寫存儲過程的SQL語句是一種業(yè)務(wù)規(guī)則的腳本,當(dāng)業(yè)務(wù)規(guī)則發(fā)生變化時,無須對業(yè)務(wù)組件使用編譯和替換的高超魔法——須知越高超越危險。存儲過程將這個問題簡單的化解。
b) 可以直接應(yīng)用數(shù)據(jù)庫內(nèi)置的權(quán)限管理。如果不使用存儲過程,一個業(yè)務(wù)規(guī)則能不能運行,需要在業(yè)務(wù)組件里進行控制,因此業(yè)務(wù)組件中需要另起一套權(quán)限控制框架,這個框架和數(shù)據(jù)庫本身的權(quán)限控制是同構(gòu)的,正所謂疊床架屋,多此一舉。而利用數(shù)據(jù)庫的權(quán)限管理則從源頭把關(guān),直接安全。
c) 語言純粹。和在 .net/java代碼中混雜數(shù)據(jù)庫操控的笨拙代碼相比,存儲過程完全使用SQL語言,盡管各種數(shù)據(jù)庫的方言有異。提取數(shù)據(jù),處理數(shù)據(jù),保存結(jié)果的工作用同一種語言融在一處,無疑屬于更好的編程風(fēng)格。這個問題可以類比于 ASP->ASP.NET,從代碼混雜到各自純粹。.net3.0 意圖使用 DLink技術(shù)把 SQL 語言變成編程語言的一部分,想法是類似的。
但相較于各自純粹而言,那種在 VB里直接寫 FROM SELECT WHERE的做法恐怕也不可取。雖然我完全支持 <Table("Employee")> Class Employee 這種語言級別的 ORM。
d) 便于移植。和以前數(shù)據(jù)庫山頭林立的年代不同,現(xiàn)在我們更不放心的是平臺。如果我們使用 .net 平臺,意味著我們放棄了傳說中更堅強的Linux —— mono 恐怕還靠不住。如果我們使用 java,雖然得到了跨平臺的好處,但是只能做 bs,而在 Windows平臺又失去了 .net 所具備的高速度——后者是致命的。另外,如果客戶需要一個對配置要求不高反應(yīng)又不遲鈍的 cs 程序,也許用 Delphi 更能符合用戶的愿望。我們通常要在 win32/.net/java 平臺之間做出選擇。而這種選擇沒有回頭路可走。如果我們把數(shù)據(jù)庫操作代碼放在數(shù)據(jù)訪問層,把業(yè)務(wù)規(guī)則代碼放在業(yè)務(wù)層,一切都特定于某種平臺了。相反,如果使用存儲過程,業(yè)務(wù)規(guī)則部署于數(shù)據(jù)庫中,數(shù)據(jù)訪問層和業(yè)務(wù)層都只剩下一個殼,代碼大為簡化,使用任何平臺的語言來改寫都可以快速完工。想想看,一周的時間就可以在一個新平臺寫完前端,何樂而不為呢。
e) 方便的使用數(shù)據(jù)庫的事務(wù)機制。在程序語言中控制事務(wù),無論哪個平臺都有隔靴搔癢之感,而在純粹的SQL語言則顯得心應(yīng)手,渾然一體。
就我理解,像便于換庫這種理由,從歷史原因來考察,是軟件開發(fā)傷口上的痛。這個問題一直到ODBC等等方案風(fēng)行之后才得到解除。以前特定于某種類型的數(shù)據(jù)庫的程序,升遷所花費的代價是非常昂貴的。這次大動蕩給一代人投下了心理陰影,提到特定于某種數(shù)據(jù)庫時,便產(chǎn)生一朝被蛇咬,十年怕井繩的恐懼心理。
類似的事情還有函數(shù)嵌套函數(shù)。結(jié)構(gòu)化程序設(shè)計中,一個關(guān)鍵性的觀念就是功能獨立,其程序功能被劃分為多個獨立的功能函數(shù)。但有些功能是受雇于另外的子功能的,并非第一級子功能。例如,通常一個遞歸會寫成兩塊,一塊發(fā)起遞歸,一塊遞歸,在類似無嵌套函數(shù)的語言中,發(fā)起遞歸會變成一個函數(shù),遞歸過程又作為一個函數(shù),這兩個函數(shù)地位平行。而在支持嵌套函數(shù)的語言中,遞歸過程可以實現(xiàn)為一個子函數(shù),嵌入進發(fā)起函數(shù)。但是現(xiàn)在的語言多不支持嵌套,因為OO浪潮使人們畏懼結(jié)構(gòu)化,即使本不相左的交集。唯最近微軟披露的 .net 3.0 框架計劃引入該機制,也許是Anders很懷念Pascal,當(dāng)然,更大的可能是微軟也意識到了這個問題。