對於這個問題,開始的設想比較簡單,大致過程是:把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#裡的規則運算式就很好用了。不過經過重寫這些模式識別,也把以前學的編譯原理好好複習了一遍,對有些概念的理解更深入一些,只怪當初學的還不夠精啊。呵呵。
國外較成熟的sql翻譯器參考:http://www.microsoft.com/downloads/thankyou.aspx?familyId=E35CEE88-C919-463F-B020-81468CD231DA&displayLang=en
http://www.ispirer.com/products