和風(fēng)細(xì)雨

          世上本無難事,心以為難,斯乃真難。茍不存一難之見于心,則運(yùn)用之術(shù)自出。

          使用正則表達(dá)式解析SQL語句

          本文詳細(xì)代碼請見:
          http://www.aygfsteel.com/sitinspring/archive/2008/03/14/186372.html

           

          問題:將左邊的SQL語句解析成右邊的形式

          Select c1,c2,c3 From t1,t2,t3 Where condi1=5 and condi6=6 or condi7=7 Group  by g1,g2,g3 order  by g2,g3

          select
               c1,
              c2,
              c3
          from
               t1,
              t2,
              t3
          where
               condi1=5 and
               condi6=6 or
               condi7=7
          group by
               g1,
              g2,
              g3
          order by
               g2,
              g3

          按關(guān)鍵字找出SQL語句中各部分

          我們閱讀SQL語句會把整句分來成列,表,條件,分組字段,排序字段來理解,解析SQL的目的也是這樣.
          分解SQL語句有規(guī)律可循,以列為例,它必定包含在select和from之間,我們只要能找到SQL語句中的關(guān)鍵字select和from,就能找到查詢的列.
          怎么找到select和from之間的文字呢?其實(shí)一個正則表達(dá)式就能解決:(select)(.+)(from),其中第二組(.+)代表的文字就是select和from之間的文字.
          程序見右邊.

          /**
           * 從文本text中找到regex首次匹配的字符串,不區(qū)分大小寫
           * @param regex: 正則表達(dá)式
           * @param text:欲查找的字符串
           * @return regex首次匹配的字符串,如未匹配返回空
           */
          private static String getMatchedString(String regex,String text){
            Pattern pattern=Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
             
              Matcher matcher=pattern.matcher(text);

              while(matcher.find()){
                return matcher.group(2);
              }
             
              return null;
          }

          解析函數(shù)分析

          private static String getMatchedString(String regex,String text){
            Pattern pattern=Pattern.compile(regex,Pattern.CASE_INSENSITIVE);
             
              Matcher matcher=pattern.matcher(text);

              while(matcher.find()){
                return matcher.group(2);
              }
             
              return null;
          }

          左邊的這個函數(shù),第一個參數(shù)是擬定的正則表達(dá)式,第二個是整個SQL語句.
          當(dāng)正則表達(dá)式為(select)(.+)(from)時(shí),程序?qū)⒃赟QL中查找第一次匹配的地方(有Pattern.CASE_INSENSITIVE的設(shè)置,查找不區(qū)分大小寫),如果找到了則返回模式中的第二組代表的文字.
          如果sql是select a,b from tc,則返回的文字是a,b.

          選擇的表對應(yīng)的查找正則表達(dá)式

          選擇的表比較特殊,它不想選擇的列一樣固定處于select和from之間,當(dāng)沒有查找條件存在時(shí),它處于from和結(jié)束之間;當(dāng)有查找條件存在時(shí),它處于from和where之間.
          因此查詢函數(shù)寫為右邊的形式:

          /**
           * 解析選擇的表
           *
           */
          private void parseTables(){
              String regex="";  
             
              if(isContains(sql,"\\s+where\\s+")){
                regex="(from)(.+)(where)";  
              }
              else{
                regex="(from)(.+)($)";  
              }
             
              tables=getMatchedString(regex,sql);
          }


          isContains函數(shù)

          isContains函數(shù)用于在lineText中查找word,其中不區(qū)分大小些,只要找到了即返回真.

          /**
           * 看word是否在lineText中存在,支持正則表達(dá)式
           * @param lineText
           * @param word
           * @return
           */
          private static boolean isContains(String lineText,String word){
            Pattern pattern=Pattern.compile(word,Pattern.CASE_INSENSITIVE);
            Matcher matcher=pattern.matcher(lineText);
            return matcher.find();
          }

          解析查找條件的函數(shù)

          private void parseConditions(){
              String regex="";  
             
              if(isContains(sql,"\\s+where\\s+")){
                // 包括Where,有條件
               
                if(isContains(sql,"group\\s+by")){
                  // 條件在where和group by之間
                  regex="(where)(.+)(group\\s+by)"; 
                }
                else if(isContains(sql,"order\\s+by")){
                  // 條件在where和order by之間
                  regex="(where)(.+)(order\\s+by)"; 
                }
                else{
                  // 條件在where到字符串末尾
                  regex="(where)(.+)($)"; 
                }       
              }
              else{
                // 不包括where則條件無從談起,返回即可
                return;
              }
             
              conditions=getMatchedString(regex,sql);
          }

          解析GroupBy的字段

          private void parseGroupCols(){
              String regex="";  
             
              if(isContains(sql,"group\\s+by")){
                // 包括GroupBy,有分組字段

                if(isContains(sql,"order\\s+by")){
                  // group by 后有order by
                  regex="(group\\s+by)(.+)(order\\s+by)"; 
                }
                else{
                  // group by 后無order by
                  regex="(group\\s+by)(.+)($)"; 
                }     
              }
              else{
                // 不包括GroupBy則分組字段無從談起,返回即可
                return;
              }
             
              groupCols=getMatchedString(regex,sql);
          }


          解析OrderBy的字段

          private void parseOrderCols(){
              String regex="";  
             
              if(isContains(sql,"order\\s+by")){
                // 包括order by,有分組字段
                regex="(order\\s+by)(.+)($)";                 
              }
              else{
                // 不包括GroupBy則分組字段無從談起,返回即可
                return;
              }
               
              orderCols=getMatchedString(regex,sql);
          }

          得到解析后的各部分

          按以上解析方法獲得了列,表,條件,分組條件,排序條件各部分之后,它們會存儲到各個成員變量中.
          注意這些成員變量的原值都是null,如果在SQL語句中能夠找到對應(yīng)的部分的話它們將借助getMatchedString獲得值,否則還是null.我們通過判斷這些成員變量是否為空就能知道它對應(yīng)的部分是否被解析出來.

           /**
             * 待解析的SQL語句
             */
            private String sql;
           
            /**
             * SQL中選擇的列
             */
            private String cols;
           
            /**
             * SQL中查找的表
             */
            private String tables;
           
            /**
             * 查找條件
             */
            private String conditions;
           
            /**
             * Group By的字段
             */
            private String groupCols;
           
            /**
             * Order by的字段
             */
            private String orderCols;

          取得不需要單行顯示時(shí)的SQL語句

          進(jìn)展到這一步,SQL語句中列,表,條件,分組條件,排序條件各部分都被獲取了出來,這時(shí)把它們重新組合一下就能得到整理后的SQL語句.
          如下面的SQL語句將變成右邊的部分(先使靜態(tài)成員isSingleLine=false):
          Select c1,c2,c3 From t1,t2,t3 Where condi1=5 and condi6=6 or condi7=7 Group  by g1,g2,g3 order  by g2,g3

          select
               c1,c2,c3
          from
               t1,t2,t3
          where
               condi1=5 and condi6=6 or condi7=7
          group by
               g1,g2,g3
          order by
               g2,g3


          進(jìn)一步解析

          有時(shí)我們需要把列,表,條件,分組條件,排序條件單行顯示以方便查看或加上注釋,這就要求我們對列,表,條件,分組條件,排序條件等進(jìn)行進(jìn)一步解析.
          初看解析很方便,以固定的分隔符劈分即可,但需要注意的是查詢條件中分隔符有and和or兩種,如果貿(mào)然分隔會使重新組合時(shí)使SQL失真.
          推薦一種做法,我們可以在分隔符后加上一個標(biāo)志如空行,然后再以這個標(biāo)志來劈分.這樣就不會使SQL失真了.
          請見下頁的getSplitedParagraph函數(shù).

          getSplitedParagraph函數(shù)

          private static List<String> getSplitedParagraph(String paragraph,String splitStr){
            List<String> ls=new ArrayList<String>();   
           
            // 先在分隔符后加空格
            Pattern p = Pattern.compile(splitStr,Pattern.CASE_INSENSITIVE);

            Matcher m = p.matcher(paragraph);
            StringBuffer sb = new StringBuffer();

            boolean result = m.find();
            while (result) {
              m.appendReplacement(sb, m.group(0) + Crlf);
              result = m.find();
            }
            m.appendTail(sb);
           
            // 再按空格斷行
            String[] arr=sb.toString().split("[\n]+");
            for(String temp:arr){
              ls.add(FourSpace+temp+Crlf);
            }
           
            return ls;
          }

          處理結(jié)果

          把靜態(tài)成員變量isSingleLine=true后我們來看看執(zhí)行結(jié)果:
          select
               c1,
              c2,
              c3
          from
               t1,
              t2,
              t3
          where
               condi1=5 and
               condi6=6 or
               condi7=7
          group by
               g1,
              g2,
              g3
          order by
               g2,
              g3

          小結(jié)

          從這個例子中我們體會了分治的思想:分治是把一個大問題分解成小問題,然后分別解決小問題,再組合起來大問題的解決方法就差不多了.這種思想在工程領(lǐng)域解決問題時(shí)很普遍,我們要學(xué)會使用這種思想來看待,分析和解決問題,不要貪多求大,結(jié)果導(dǎo)致在大問題面前一籌莫展.
          其次我們可以從這個例子中學(xué)習(xí)找規(guī)律,然后借助規(guī)律的過程,現(xiàn)實(shí)世界千變?nèi)f化,但都有規(guī)律可循,只要我們找到了規(guī)律,就等于找到了事物之門的鑰匙.
          接下了我們復(fù)習(xí)了正則表達(dá)式用于查找的方法,以前的正則表達(dá)式學(xué)習(xí)多用于驗(yàn)證匹配,其實(shí)這只是正則表達(dá)式的一部分功能.
          最后從解析條件成單行的過程中,我們可以學(xué)習(xí)到一種解決問題的技巧,即當(dāng)現(xiàn)實(shí)中的規(guī)律存在變數(shù)時(shí)加入人為設(shè)置的規(guī)律,這有時(shí)能使我們更好更快的解決問題.

          posted on 2008-03-19 22:00 和風(fēng)細(xì)雨 閱讀(9668) 評論(4)  編輯  收藏 所屬分類: 正則表達(dá)式

          評論

          # re: 使用正則表達(dá)式解析SQL語句 2008-04-22 02:44 java 開發(fā)

          謝謝,很有幫助  回復(fù)  更多評論   

          # re: 使用正則表達(dá)式解析SQL語句 2008-11-25 22:46 bza

          謝謝  回復(fù)  更多評論   

          # re: 使用正則表達(dá)式解析SQL語句 2011-06-22 17:12 青青園中葵

          復(fù)合的sql就不能處理了,這樣太簡單  回復(fù)  更多評論   

          # re: 使用正則表達(dá)式解析SQL語句 2012-10-08 19:44 郭濤

          的確不錯,很有幫助。
          @青青園中葵  回復(fù)  更多評論   


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 城固县| 哈密市| 读书| 阜阳市| 蒲城县| 平泉县| 武陟县| 黄龙县| 循化| 特克斯县| 冷水江市| 双辽市| 宜宾市| 华蓥市| 东方市| 安仁县| 嵊州市| 安远县| 惠东县| 汪清县| 手游| 筠连县| 吉安县| 革吉县| 郎溪县| 东乡族自治县| 新竹市| 永新县| 凤冈县| 涞源县| 广水市| 泽库县| 洪江市| 民勤县| 武川县| 上虞市| 桦川县| 肇州县| 宁国市| 依兰县| 酉阳|