對於這個問題,開始的設想比較簡單,大致過程是:把Sql語句中不相同的關鍵字和函數名替換掉,如Oracle中的To_Date換成SqlServer的Convert,就可以在SqlServer上執行了.對一些簡單的Sql語句這樣確實可以,可是對復雜的應用來說,Sql語句可能多層嵌套,函數也有多層嵌套,如果只是簡單的替換,代碼中必然會有無數的if else,並且出錯後的修改和調試幾乎是不可能的。
通過對Oracle和SqlServer兩種數據庫的Sql語法的研究比較,認為必須采用語法分析,把Sql語句解析為一棵語法樹,然後再按照語法的轉換規則把sql語句轉換到SqlServer上可執行的語句。要實現這樣的功能,需要用到的模式有:
1. INTERPRETER(解釋器)—類行為型模式:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。通過實現解釋器模式,把要執行的Sql語句解釋為Sql的語法樹。例如一個Select語句的結構如下
從這張結構圖中可以看到,Sql語句可能出現非常復雜的組合結構,如果不使用語法樹表示,很難實現不同數據庫平台的轉換。
2. COMPOSITE(組合)—對象結構型模式:將對象組合成樹形結構以表示“部分-整體”的層次結構。C o m p o s i t e使得用戶對單個對象和組合對象的使用具有一致性。
從上面的Sql語句的語法結構可以看到一個查詢語句可能是很簡單的select * from ATable,也可能在sql裡面又包含其他的Sql語句。按照組合優先於繼承的規則,並沒有給單獨的Sql和復合的Sql語句創建不同的類,而是在內部組合並遞歸引用自己的定義,對訪問語法樹的客戶代碼來說,並不需要了解所訪問的Sql語句是否存在復合結構。
3. VISITOR(訪問者)—對象行為型模式:表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這些元素的新操作。
前面已經通過解釋器模式解析Sql語法,用組合模式來存儲解析的語法樹,但是我們所需要的不僅如此。還要按照SqlServer的語法結構把語法樹上的各個節點重新組合,最終輸出SqlServer上可以執行的Sql語句。例如:Oracle中的一句連接查詢select a.*,b.* from a,b where a.id=b.id(+),在SqlServer中對應的語句應該是select a.*,b.* from a left join b on a.id=b.id。
從這個簡單的例子中可以看到對於表的左連接或右連接,兩種數據庫的語法結構存在較大的差異。如果是在TSql類中寫某個方法,由這個方法遍歷語法樹上的每個節點,並按照SqlServer的語法結構組合所需要的結果,是可以達到這個目的的。可是如果需要從這棵語法樹導出其它數據庫上如Sybase可執行的sql語句呢,那又要在TSql類中再增加新的遍歷算法,所有的相關代碼又要重新編譯。
通過采用訪問者模式,把遍歷節點時組合語法樹節點的算法封裝再訪問者的方法中,如SqlServer的語法就是一個TSqlServerVisitor類,語法樹遍歷每個節點時,都會調用它的Visit方法,全部訪問完後即可通過GetSql得到所需要的Sql語句。這時如果我們需要轉換到Sybase,只需要再實現一個TSybaseVisitor類,並傳給語法樹,就可以得到Sybase的sql語句了。
4.有限狀態機--單詞和關鍵字的識別.在解析一句sql語句前,先要把其中的字符、數字、關鍵字和函數等語法元素識別出來。這顯然不能簡單的用字符定位等來判斷,而必須用狀態機來識別不同的規則表達式。這方面現在c#裡的規則表達式就很好用了。不過經過重寫這些模式識別,也把以前學的編譯原理好好復習了一遍,對有些概念的理解更深入一些,只怪當初學的還不夠精啊。呵呵。