好一段時(shí)間沒寫博客了,這次我們來一起談?wù)凷QL文件執(zhí)行器的功能實(shí)現(xiàn),在ERP軟件升級(jí)時(shí)往往在客戶端程序更新的同時(shí)也要對(duì)數(shù)據(jù)庫進(jìn)行升級(jí),ERP程序開發(fā)人員會(huì)對(duì)數(shù)據(jù)庫升級(jí)的執(zhí)行代碼在開發(fā)的過程中以SQL文件的形式記錄下來或者保存到特定格式的文件中供軟件升級(jí)時(shí)使用,有些ERP軟件會(huì)附帶開發(fā)數(shù)據(jù)庫升級(jí)工具來方便實(shí)施人員執(zhí)行軟件升級(jí)操作或者ERP軟件內(nèi)置數(shù)據(jù)庫升級(jí)功能,不管使用什么樣的方式能達(dá)到軟件升級(jí)的目的就是好方法,這次我們就來剝離這部分的功能來實(shí)現(xiàn)一個(gè)SQL文件執(zhí)行器,不特定于SQL文件,只要文件里面包含有SQL語法,而且能正常得到執(zhí)行,在本執(zhí)行器中就能正確的執(zhí)行它(本次我們主要爭對(duì)SQL文件,其他格式文件只要把相關(guān)控制去掉就好了,只是得不到很好的控制比較亂,或者你有更好的方法)。介紹性引入就到此為止了,接下來我們開始進(jìn)入主題,研究一下SQL執(zhí)行器的原理及設(shè)計(jì)思路吧。
一、SQL文件執(zhí)行器原理分析
???????? 在分析原理之前,我們先來規(guī)范一下SQL文檔的書寫,我們?cè)谝粋€(gè)SQL語句結(jié)束的時(shí)候換行來個(gè)GO關(guān)鍵字再換行繼續(xù)書寫下一個(gè)SQL語句,遇到“USE [數(shù)據(jù)庫名] ”語句時(shí)希望能把此語句單獨(dú)放一行。做到以上書寫規(guī)范我們開始原理分析:
1.上述SQL規(guī)范居然都做到了但是SQL文件里面的內(nèi)容還不是我想要的,我想重新規(guī)范一下我的SQL文檔,我想理想化它,所以我需要對(duì)SQL文件進(jìn)行重新洗牌,我想我應(yīng)該一行一行的閱讀它,并把它提取出來去掉前后的空格,我應(yīng)該重組改文件的內(nèi)容并且在讀取每一行的同時(shí)在其末尾寫入一個(gè)換行回車符,而且我還想統(tǒng)計(jì)出每一個(gè)SQL文件中的SQL語句的個(gè)數(shù)(循環(huán)時(shí)會(huì)用到),我用GO關(guān)鍵字來標(biāo)記了SQL語句的個(gè)數(shù),在閱讀到一行去掉首尾空格后只剩下不區(qū)分大小寫的GO時(shí)我的統(tǒng)計(jì)會(huì)+1(統(tǒng)計(jì)從1開始),哦,這還永遠(yuǎn)不夠,我還沒考慮到USE關(guān)鍵字的處理,通常會(huì)選擇一個(gè)數(shù)據(jù)庫來執(zhí)行相應(yīng)的SQL文件,USE關(guān)鍵字在切換著數(shù)據(jù)庫,我需要在檢測到USE語句時(shí)切換數(shù)據(jù)庫為USE后面跟著的數(shù)據(jù)庫來執(zhí)行后面的代碼,所以我要把USE語句單獨(dú)剝離開來以待做特殊的處理,往往我們寫SQL文件的時(shí)候在USE語句的前后都不帶GO關(guān)鍵字的,我需要給它加上。 有一個(gè)需要注意的地方:USE [數(shù)據(jù)庫名]? 后面直接跟著SQL語句的(沒有回車換行),這種寫法在語法上是完全正確的,但是看上去就不是很美觀了,這種方式我這邊就不做處理了,請(qǐng)遵守上述規(guī)范吧,再處理下去程序性能就嚴(yán)重下降啦,本程序在數(shù)據(jù)庫切換時(shí)是從USE 之后的字符開始到回車換行符結(jié)束來取數(shù)據(jù)庫名的,這種寫法會(huì)引發(fā)SQL異常。 我需要構(gòu)建一個(gè)這樣的方法。
2.在第一點(diǎn)里面我們對(duì)SQL文件進(jìn)行了格式化,現(xiàn)在開始我希望以GO關(guān)鍵字作為分割點(diǎn),把SQL文件里面的SQL語法進(jìn)行分段,我希望一段段的得到執(zhí)行并返回執(zhí)行結(jié)果,此時(shí)我需要一個(gè)循環(huán)來遍歷文件中的SQL語句并執(zhí)行它。本工具名字叫做SQL文件執(zhí)行器很顯然是爭對(duì)批量SQL文件的處理的,所以一個(gè)循環(huán)是永遠(yuǎn)不夠的,我還得在外面再套一個(gè)循環(huán)來遍歷所有的SQL文件,對(duì)每個(gè)SQL文件進(jìn)行分析并遍歷其中的SQL語句執(zhí)行它,這樣就達(dá)到我們的目的了。貌似還有一個(gè)問題未處理,比如在執(zhí)行到一半的時(shí)候我不執(zhí)行了需要強(qiáng)制停止他這如何是好呢,強(qiáng)制關(guān)閉程序很顯然是不可取的很容易引發(fā)未知的數(shù)據(jù)庫異常或者造成數(shù)據(jù)丟失這些狀況都是我們不想看到的,那么有什么好辦法呢?此時(shí)我啟用了臭名遠(yuǎn)揚(yáng)的goto語句來從深層嵌套循環(huán)中跳出循環(huán),我讓他在用戶發(fā)出停止指令后在執(zhí)行完當(dāng)前的SQL語法段后跳出循環(huán),從而停止接下來的SQL語法的執(zhí)行,這樣子保障了SQL數(shù)據(jù)的安全,在SQL文件執(zhí)行的期間,程序是不允許關(guān)閉的,除非向程序發(fā)出停止指令,并成功停止的時(shí)候,才允許程序關(guān)閉。
很簡單的一個(gè)程序,我就大致的做以上兩點(diǎn)的原理分析吧,接下來我上傳下我的程序界面設(shè)計(jì)圖吧,大家參考下:
接下來我貼上來一些主要源代碼供參考,代碼可能看上去有些難懂,表述性不是很好,期待大家來改進(jìn)它:
這是對(duì)SQL文件中的語法進(jìn)行重新洗牌的方法
/// <summary> /// 讀取文件內(nèi)容(SQL關(guān)鍵字特殊處理主要針對(duì)GO關(guān)鍵字) /// </summary> /// <param name="path">文件路徑</param> /// <param name="keywords">關(guān)鍵字</param> /// <param name="str">輸出字符串</param> /// <param name="i">keywords的個(gè)數(shù)</param> public void FileReader(string path,string keywords,out int i,out string str) { bool useplusgo = false;//use后面是否跟著go bool goplususe = false;//use前面是否存在go StreamReader sr = new StreamReader(path, Encoding.GetEncoding("GB2312")); //str = sr.ReadToEnd(); string s = null; string temp = null; int x = 0; while ((temp = sr.ReadLine()) != null) { if (temp.Trim().ToUpper() == keywords.ToUpper()) { useplusgo = false;//use語句后面跟著go則關(guān)閉use判斷 goplususe = true;//use前面存在go x++; s += "\r\n" + keywords + "\r\n \r\n"; } else if (temp.Trim().Length >= 4 && temp.Trim().ToUpper().Substring(0, 4).Trim() == "USE") { temp = temp.Trim().Replace("[", "").Replace("]", ""); //如果use前面不存在go則加上 if (!goplususe && s != "\r\n" && s != null) { x++; s += "\r\n" + keywords + "\r\n\r\n" + temp + "\r\n"; } else { s += temp + "\r\n"; } useplusgo = true; } else { goplususe = false;//go后面不跟use關(guān)閉判斷 //如果use后面不跟go則加上go if (useplusgo) { x++; s += "\r\n" + keywords + "\r\n" + temp + "\r\n"; useplusgo = false;//關(guān)閉use判斷 } else { s += temp + "\r\n"; } } } i = x; str = s; sr.Close();//關(guān)閉當(dāng)前打開的文件 }
這個(gè)是處理USE語句的方法
/// <summary> /// 獲取use后面的數(shù)據(jù)庫名稱 /// </summary> /// <param name="str">use字符串行</param> /// <returns></returns> public string UseStatementProcessing(string str) { string[] strSplit = Regex.Split(str, "\r\n", RegexOptions.IgnoreCase); int x = strSplit.Length; string s = ""; if (/*strSplit[0].ToUpper().IndexOf("USE ", 0) >= 0*/Regex.IsMatch(strSplit[0].ToUpper(), "USE ", RegexOptions.IgnoreCase)) { s = strSplit[0].Substring(4, strSplit[0].Length - 4).Trim(); } else if (/*strSplit[1].ToUpper().IndexOf("USE ", 0) >= 0*/Regex.IsMatch(strSplit[1].ToUpper(), "USE ", RegexOptions.IgnoreCase)) { s = strSplit[1].Substring(4, strSplit[1].Length - 4).Trim(); } else { s = ""; } return s; }
這次話題就到此為止吧,這程序比較簡單,大家可以寫寫玩,當(dāng)作練練手也不錯(cuò),主要在于文件的操作和字符串的處理。
?
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
