新聞中心
一、開(kāi)發(fā)初衷

相信大家在平時(shí)寫(xiě)SQL語(yǔ)句的時(shí)候,為了節(jié)省時(shí)間,都不太喜歡把小指放到Shift鍵上打出關(guān)鍵字的大寫(xiě)形式,比如建數(shù)據(jù)庫(kù),直接就create database...了,而不是CREATE DATABASE,反正SQL SERVER都可以執(zhí)行語(yǔ)句,所以也就無(wú)所謂代碼的效果了。
的確如此,但是編程都有規(guī)范一說(shuō),在SQL的編寫(xiě)中,將關(guān)鍵字大寫(xiě)就是一個(gè)規(guī)范,所以如果有這樣的工具能夠方便象我也是這樣的懶人,批量將關(guān)鍵字替換成大寫(xiě)那就好了,不過(guò)飯來(lái)張口、衣來(lái)伸手可不是什么好習(xí)慣,所以還是DIY吧。。。
二、開(kāi)發(fā)思路
建立Winform應(yīng)用程序,依據(jù)需求此工具需要實(shí)現(xiàn)三個(gè)方面:
1、打開(kāi)對(duì)話框——選擇要處理的SQL文件路徑;
2、保存對(duì)話框——選擇處理完成后輸出文件的路徑;
3、核心功能——用正則表達(dá)式查找SQL文件中的關(guān)鍵字,如有匹配則將其轉(zhuǎn)換為大寫(xiě)。
三、開(kāi)發(fā)過(guò)程
俗話說(shuō):計(jì)劃趕不上變化。本來(lái)計(jì)劃一兩個(gè)小時(shí)就能夠搞定,但是還是足足折騰了一個(gè)下午,實(shí)在有些汗顏,下面就詳細(xì)敘述一下過(guò)程吧:
1、搜集SQL關(guān)鍵字
要說(shuō)SQL關(guān)鍵字,除了大家常見(jiàn)的CRUD相關(guān)的語(yǔ)句,還有FUNCTION、CURSOR、PROCEDURE、系統(tǒng)內(nèi)置VARIABLE、VIEW、SCHEMA等等等等,總之一大堆,而且貌似也超出關(guān)鍵字的范疇了,要想完全搞定這么多東西,俺實(shí)在只能說(shuō)無(wú)能為力了,所以我只搜集了大家平時(shí)經(jīng)常用到的一些關(guān)鍵字,在下面的代碼中會(huì)看到。
PS:當(dāng)然如果園子里哪位能夠做出一個(gè)覆蓋所有SQL那樣強(qiáng)大的來(lái),小弟一定前往拜讀。
2、SQL文件語(yǔ)句的遍歷
不像ORACLE里的SQL PLUS那樣難用的東西(主要是俺太菜了,經(jīng)常出紕漏,很難一下就寫(xiě)對(duì)SQL),在SQL SERVER中SQL語(yǔ)句可以上下鍵隨意更改內(nèi)容或格式,而且語(yǔ)句也不用強(qiáng)制分號(hào)作為結(jié)尾,所以復(fù)雜的時(shí)候幾行甚至十幾行才是一個(gè)完整的SQL語(yǔ)句,所以一開(kāi)始我就嘗試從頭到尾遍歷整個(gè)文件貌似有些不太現(xiàn)實(shí),最后的方案是采用了按行劃分,以一行為一個(gè)數(shù)組進(jìn)行處理,代碼如下:
- //先以行作為劃分,得到一個(gè)數(shù)組
- Regex rowReg = new Regex("\r\n");
- string[] strRow = rowReg.Split("轉(zhuǎn)換的SQL源文件內(nèi)容");
3、現(xiàn)在依然不能急著處理strRow數(shù)組中的每一個(gè)元素,因?yàn)樵刂杏锌赡軙?huì)出現(xiàn)" create database "這樣的情況,即關(guān)鍵字兩側(cè)有一個(gè)或多個(gè)空格字符,如果不進(jìn)行統(tǒng)一處理,會(huì)給后面的替換帶來(lái)很大的麻煩,所以這里再用一個(gè)正則表達(dá)式,使空格通通變成一個(gè),隨后再以空格作為分割符,這樣才算得到了我們真正需要進(jìn)行匹配替換操作的數(shù)組:
- for (int i = 0; i < strRow.Length; i++)
- {
- strRow[i] = Regex.Replace(strRow[i], @"\s+", " ");
- string[] strRowDetail = strRow[i].Split(new char['\0']);
- }
4、下面循環(huán)遍歷strRowDetail數(shù)組中的每一項(xiàng):
- for (int j = 0; j < strRowDetail.Length; j++)
- {
- if (regex.regRow.IsMatch(strRowDetail[j]))
- {
- Match match = regex.Match(strRowDetail[j]);
- ...
- }
- }
為什么暫時(shí)省略了后面的代碼呢,因?yàn)檫@樣寫(xiě)考慮是不周全的,試想如果一個(gè)SQL語(yǔ)句如:
- create table student
- (
- cno varchar(50),
- ...
- )
這樣關(guān)鍵字一個(gè)是一個(gè)的話,那匹配起來(lái)自然沒(méi)有問(wèn)題,但很多時(shí)候事情并沒(méi)有那么簡(jiǎn)單,有些SQL語(yǔ)句包含了很多關(guān)鍵字。
最典型的就是日期類型的操作,比如:
- --求本月天數(shù):select day(dateadd(mm,1,getdate())-day(getdate()));
這樣的一個(gè)SQL語(yǔ)句我們以空格劃分后,那么數(shù)組的第二個(gè)元素會(huì)是day(dateadd(mm,1,getdate())-day(getdate())),如果進(jìn)行上述的Match匹配就只會(huì)將第一個(gè)day變?yōu)榇髮?xiě),而其他則沒(méi)有得到處理。
進(jìn)行了一定的測(cè)試后,我轉(zhuǎn)用了MatchCollection類,代碼如下(注:tbSource指的是顯示SQL代碼的文本框):
- for (int j = 0; j < strRowDetail.Length; j++)
- {
- //regex.regRow指的是尋找匹配關(guān)鍵字的正則表達(dá)式,在代碼下載中會(huì)看到
- //首先查看strRowDetail[j]是否能夠匹配
- if (regex.regRow.IsMatch(strRowDetail[j]))
- {
- //若能匹配則記錄其匹配的內(nèi)容
- MatchCollection mc = regex.regRow.Matches(strRowDetail[j]);
- //遍歷匹配的集合
- for (int k = 0; k < mc.Count; k++)
- {
- //循環(huán)替換匹配的項(xiàng)
- strRowDetail[j] = strRowDetail[j].Replace(
- mc[k].Value, mc[k].Value.ToUpper());
- }
- //附加替換完成后的內(nèi)容
- this.tbSource.Text += strRowDetail[j] + " ";
- }
- //不匹配則直接附加原來(lái)的內(nèi)容
- else
- {
- this.tbSource.Text += strRowDetail[j] + " ";
- }
- }
- //因?yàn)樽畛跏且孕蟹指畹?,所以這里要附加\r\n,從而保持其原來(lái)的格式
- this.tbSource.Text += "\r\n";
5、最后用StreamWriter將得到的內(nèi)容寫(xiě)入文件,這里除了編碼問(wèn)題貌似也沒(méi)啥好說(shuō)的了:
- StreamWriter writer = new StreamWriter(this.tbExportPath.Text, false,
- Encoding.Default);
- writer.WriteLine(this.tbSource.Text);
- writer.Flush();
四、效率測(cè)試
細(xì)心的你在閱讀代碼的過(guò)程中可能已經(jīng)發(fā)現(xiàn)我在大量的字符串處理操作時(shí)一直在使用普通的字符串"+="這樣的方法,其實(shí)這也是我為我的效率小測(cè)驗(yàn)準(zhǔn)備的,這下可以再切實(shí)的體會(huì)一下StringBuilder對(duì)效率的提升。
要測(cè)試那還是要請(qǐng)出我們的Stopwatch了,相信大家都會(huì)的了,下面的代碼直接無(wú)視:
- StreamWriter writer = new StreamWriter(this.tbExportPath.Text, false, Encoding.Default);
- writer.WriteLine(this.tbSource.Text);
- writer.Flush();
如果使用原來(lái)普通字符串連接的方法,足足等了我5347毫秒!
而如果使用StringBuilder對(duì)象作字符串操作,并且為提高for循環(huán)效率,避免每次重復(fù)計(jì)算數(shù)組長(zhǎng)度,先將數(shù)組的長(zhǎng)度存儲(chǔ)在變量中,優(yōu)化代碼如下:
- int strRowDetailLength = strRowDetail.Length;
- for (int j = 0; j < strRowDetailLength; j++)
- {
- if (regex.regRow.IsMatch(strRowDetail[j]))
- {
- MatchCollection mc = regex.regRow.Matches(strRowDetail[j]);
- int mcCount = mc.Count;
- for (int k = 0; k < mcCount; k++)
- {
- strRowDetail[j] = strRowDetail[j]
- .Replace(mc[k].Value, mc[k].Value.ToUpper());
- }
- strBuilder.Append(strRowDetail[j] + " ");
- }
- else15 {
- strBuilder.Append(strRowDetail[j] + " ");
- }
- }
- strBuilder.Append("\r\n");
測(cè)試的結(jié)果是34毫秒,hoho,StringBuilder的威力果然很強(qiáng)大。。。
五、項(xiàng)目說(shuō)明
1、介于正則表達(dá)式實(shí)在是礙眼,所以就沒(méi)有貼出來(lái),說(shuō)實(shí)話我自己都不忍心看了。。。
2、麻雀雖小也要五臟俱全嘛,這個(gè)小工具在界面上使用了錢(qián)李峰同學(xué)的 美化版Winform,沒(méi)經(jīng)作者同意就亂打廣告,忘作者不要介意啊 ^_^
六、后續(xù)問(wèn)題
1、希望大家在看完后能夠指出我在解決的思路上有沒(méi)有什么問(wèn)題,有沒(méi)有什么簡(jiǎn)單的方法,因?yàn)殡m然實(shí)現(xiàn)了,但個(gè)人感覺(jué)方法上有點(diǎn)齪。。。
2、期待哪位能夠開(kāi)發(fā)出更完善更優(yōu)雅的SQL關(guān)鍵字替換工具。。。
3、如有使用問(wèn)題請(qǐng)及時(shí)留言。。。
七、項(xiàng)目下載
http://files.cnblogs.com/RockyMyx/Solution.rar
網(wǎng)站題目:SQLServer管理員不易養(yǎng)成的好習(xí)慣(附小工具)
標(biāo)題網(wǎng)址:http://fisionsoft.com.cn/article/cddedij.html


咨詢
建站咨詢
