校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃

主頁 > 知識(shí)庫 > sql字段解析器的實(shí)現(xiàn)示例

sql字段解析器的實(shí)現(xiàn)示例

熱門標(biāo)簽:在電子版地圖標(biāo)注要收費(fèi)嗎 萬利達(dá)綜合醫(yī)院地圖標(biāo)注點(diǎn) 武漢AI電銷機(jī)器人 地圖標(biāo)注如何弄全套標(biāo) 股票配資電銷機(jī)器人 實(shí)體店地圖標(biāo)注怎么標(biāo) 外呼系統(tǒng)會(huì)封嗎 電銷機(jī)器人 深圳 南京電銷外呼系統(tǒng)哪家好

用例:有一段sql語句,我們需要從中截取出所有字段部分,以便進(jìn)行后續(xù)的類型推斷或者別名字段抽取定義,請(qǐng)給出此解析方法。

想來很簡單吧,因?yàn)?sql 中的字段列表,使用方式有限,比如 a as b, a, a b...

1. 解題思路

  如果不想做復(fù)雜處理,最容易想到的,就是直接用某個(gè)特征做分割即可。比如,先截取出 字段列表部分,然后再用逗號(hào)',' 分割,就可以得到一個(gè)個(gè)的字段了。然后再要細(xì)分,其實(shí)只需要用 as 進(jìn)行分割就可以了。

  看起來好像可行,但是存在許多漏洞,首先,這里面有太多的假設(shè):各種截取部分要求必須符合要求,必須沒有多余的逗號(hào),必須要有as 等等。這明顯不符合要求了。

  其二,我們可以換一種轉(zhuǎn)換方式。比如先截取到field部分,然后先以 as 分割,再以逗號(hào)分割,然后取最后一個(gè)詞作為field。

  看起來好像更差了,截取到哪里已經(jīng)完全不知道了。即原文已經(jīng)被破壞殆盡,而且同樣要求要有 as 轉(zhuǎn)換標(biāo)簽,而且對(duì)于函數(shù)覬覦有 as 的場景,就完全錯(cuò)誤了。

  其三,最好還是自行一個(gè)個(gè)單詞地解析,field 字段無外乎幾種情況,1. 普通字段如 select a; 2. 帶as的普通字段如 select a as b; 3. 帶函數(shù)的字段如 select coalesce(a, b); 4. 帶函數(shù)且?guī)s的字段如 select coalesce(a, b) ab; 5. 函數(shù)內(nèi)帶as的字段如 select cast(a as string) b; ...   我們只需依次枚舉對(duì)應(yīng)的情況,就可以將字段解析出來了。

  看起來是個(gè)不錯(cuò)的想法。但是具體實(shí)現(xiàn)如何?

2. 具體解析實(shí)現(xiàn)

  主要分兩個(gè)部分,1. 需要定義一個(gè)解析后的結(jié)果數(shù)據(jù)結(jié)構(gòu),以便清晰描述字段信息; 2. 分詞解析sql并以結(jié)構(gòu)體返回;

  我們先來看看整個(gè)算法核心:

/**
 * 功能描述: 簡單sql字段解析器
 *
 *        樣例如1:
 *          select COALESCE(t1.xno, t2.xno, t3.xno) as xno,
 *             case when t1.no is not null then 1 else null end as xxk001,
 *             case when t2.no is not null then 1 else null end as xxk200,
 *             case when t3.xno is not null then 1 else null end as xx3200
 *             from xxk001 t1
 *               full join xxkj100 t2 on t1.xno = t2.xno
 *               full join xxkj200 t3 on t1.xno = t3.xno;
 *
 *        樣例如2:
 *          select cast(a as string) as b from ccc;
 *
 *        樣例如3:
 *          with a as(select cus,x1 from b1), b as (select cus,x2 from b2)
 *              select a.cus as a_cus from a join b on a.cus=b.cus where xxx;
 *
 *        樣例如4:
 *         select a.xno,b.xx from a_tb as a join b_tb as b on a.id = b.id
 *
 *        樣例如5:
 *          select cast  \t(a as string) a_str, cc (a as double) a_double from x
 *
 */
public class SimpleSqlFieldParser {

    /**
     * 解析一段次標(biāo)簽sql 中的字段列表
     *
     * @param sql 原始sql, 需如 select xx from xxx join ... 格式
     * @return 字段列表
     */
    public static ListSelectFieldClauseDescriptor> parse(String sql) {
        String columnPart = adaptFieldPartSql(sql);
        int deep = 0;
        ListStringBuilder> fieldTokenSwap = new ArrayList>();
        StringBuilder currentTokenBuilder = new StringBuilder();
        ListSelectFieldClauseDescriptor> fieldList = new ArrayList>();
        fieldTokenSwap.add(currentTokenBuilder);
        int len = columnPart.length();
        char[] columnPartChars = columnPart.toCharArray();
        for(int i = 0; i  len; i++) {
            // 空格忽略,換行忽略,tab忽略
            // 字符串相接
            // 左(號(hào)入棧,++deep;
            // 右)號(hào)出棧,--deep;
            // deep>0 忽略所有其他直接拼接
            // as 則取下一個(gè)值為fieldName
            // case 則直接取到end為止;
            //,號(hào)則重置token,構(gòu)建結(jié)果集
            char currentChar = columnPartChars[i];
            switch (currentChar) {
                case '(':
                    ++deep;
                    currentTokenBuilder.append(currentChar);
                    break;
                case ')':
                    --deep;
                    currentTokenBuilder.append(currentChar);
                    break;
                case ',':
                    if(deep == 0) {
                        addNewField(fieldList, fieldTokenSwap, true);
                        fieldTokenSwap = new ArrayList>();
                        currentTokenBuilder = new StringBuilder();
                        fieldTokenSwap.add(currentTokenBuilder);
                        break;
                    }
                    currentTokenBuilder.append(currentChar);
                    break;
                case ' ':
                case '\t':
                case '\r':
                case '\n':
                    if(deep > 0) {
                        currentTokenBuilder.append(currentChar);
                        continue;
                    }
                    if(currentTokenBuilder.length() == 0) {
                        continue;
                    }
                    // original_name as   --> alias
                    if(i + 1  len) {
                        int j = i + 1;
                        // 收集連續(xù)的空格
                        StringBuilder spaceHolder = new StringBuilder();
                        boolean isNextLeftBracket = false;
                        do {
                            char nextChar = columnPart.charAt(j++);
                            if(nextChar == ' ' || nextChar == '\t'
                                    || nextChar == '\r' || nextChar == '\n') {
                                spaceHolder.append(nextChar);
                                continue;
                            }
                            if(nextChar == '(') {
                                isNextLeftBracket = true;
                            }
                            break;
                        } while (j  len);
                        if(isNextLeftBracket) {
                            currentTokenBuilder.append(currentChar);
                        }
                        if(spaceHolder.length() > 0) {
                            currentTokenBuilder.append(spaceHolder);
                            i += spaceHolder.length();
                        }
                        if(isNextLeftBracket) {
                            // continue next for, function begin
                            continue;
                        }
                    }
                    if(fieldTokenSwap.size() == 1) {
                        if(fieldTokenSwap.get(0).toString().equalsIgnoreCase("case")) {
                            String caseWhenPart = CommonUtil.readSplitWord(
                                    columnPartChars, i, " ", "end");
                            currentTokenBuilder.append(caseWhenPart);
                            if(caseWhenPart.length() = 0) {
                                throw new BizException("語法錯(cuò)誤,未找到case..when的結(jié)束符");
                            }
                            i += caseWhenPart.length();
                        }
                    }
                    addNewField(fieldList, fieldTokenSwap, false);
                    currentTokenBuilder = new StringBuilder();
                    fieldTokenSwap.add(currentTokenBuilder);
                    break;
                    // 空格忽略
                default:
                    currentTokenBuilder.append(currentChar);
                    break;
            }

        }
        // 處理剩余尚未存儲(chǔ)的字段信息
        addNewField(fieldList, fieldTokenSwap, true);
        return fieldList;
    }

    /**
     * 新增一個(gè)字段描述
     *
     * @param fieldList 字段容器
     * @param fieldTokenSwap 候選詞
     */
    private static void addNewField(ListSelectFieldClauseDescriptor> fieldList,
                                    ListStringBuilder> fieldTokenSwap,
                                    boolean forceAdd) {
        int ts = fieldTokenSwap.size();
        if(ts == 1  forceAdd) {
            // db.original_name,
            String fieldName = fieldTokenSwap.get(0).toString();
            String alias = fieldName;
            if(fieldName.contains(".")) {
                alias = fieldName.substring(fieldName.lastIndexOf('.') + 1);
            }
            fieldList.add(new SelectFieldClauseDescriptor(fieldName, alias));
            return;
        }
        if(ts  2) {
            return;
        }
        if(ts == 2) {
            // original_name alias,
            if(fieldTokenSwap.get(1).toString().equalsIgnoreCase("as")) {
                return;
            }
            fieldList.add(new SelectFieldClauseDescriptor(
                    fieldTokenSwap.get(0).toString(),
                    fieldTokenSwap.get(1).toString()));
        }
        else if(ts == 3) {
            // original_name as alias,
            fieldList.add(new SelectFieldClauseDescriptor(
                    fieldTokenSwap.get(0).toString(),
                    fieldTokenSwap.get(2).toString()));
        }
        else {
            throw new BizException("字段語法解析錯(cuò)誤,超過3個(gè)以字段描述信息:" + ts);
        }
    }

    // 截取適配 field 字段信息部分
    private static String adaptFieldPartSql(String fullSql) {
        int start = fullSql.lastIndexOf("select ");
        int end = fullSql.lastIndexOf(" from");
        String columnPart = fullSql.substring(start + "select ".length(), end);
        return columnPart.trim();
    }

}

  應(yīng)該說是比較簡單的,一個(gè)for, 一個(gè) switch ,就搞定了。其他的,更多的是邏輯判定。

  下面我們來看看字段描述類的寫法,其實(shí)就是兩個(gè)字段,源字段和別名。

/**
 * 功能描述: sql字段描述 select 字段描述類
 *
 */
public class SelectFieldClauseDescriptor {
    private String fieldName;
    private String alias;

    public SelectFieldClauseDescriptor(String fieldName, String alias) {
        this.fieldName = fieldName;
        this.alias = alias;
    }

    public String getFieldName() {
        return fieldName;
    }

    public String getAlias() {
        return alias;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        SelectFieldClauseDescriptor that = (SelectFieldClauseDescriptor) o;
        return Objects.equals(fieldName, that.fieldName) 
                Objects.equals(alias, that.alias);
    }

    @Override
    public int hashCode() {
        return Objects.hash(fieldName, alias);
    }

    @Override
    public String toString() {
        return "SelectFieldClauseDescriptor{" +
                "fieldName='" + fieldName + '\'' +
                ", alias='" + alias + ''' +
                '}';
    }
}

它存在的意義,僅僅是為了使用方更方便取值,以為更進(jìn)一步的解析提供了依據(jù)。

3. 單元測試

  其實(shí)像寫這種工具類,單元測試最是方便簡單。因?yàn)樽畛醯慕Y(jié)果,我們?cè)缫杨A(yù)料,以測試驅(qū)動(dòng)開發(fā)最合適不過了。而且,基本上一出現(xiàn)不符合預(yù)期的值時(shí),很快速就定位問題了。

/**
 * 功能描述: sql字段解析器測試
 **/
public class SimpleSqlFieldParserTest {

    @Test
    public void testParse() {
        String sql;
        ListSelectFieldClauseDescriptor> parsedFieldList;
        sql = "select COALESCE(t1.xno, t2.xno, t3.xno) as xno,\n" +
                "   case when t1.xno is not null then 1 else null end as xxk001,\n" +
                "   case when t2.xno is not null then 1 else null end as xxk200,\n" +
                "   case when t3.xno is not null then 1 else null end as xx3200\n" +
                "   from xxk001 t1\n" +
                "     full join xxkj100 t2 on t1.xno = t2.xno\n" +
                "     full join xxkj200 t3 on t1.xno = t3.xno;";
        parsedFieldList = SimpleSqlFieldParser.parse(sql);
        System.out.println("result:");
        parsedFieldList.forEach(System.out::println);
        Assert.assertEquals("字段個(gè)數(shù)解析不正確",
                4, parsedFieldList.size());
        Assert.assertEquals("字段別名解析不正確",
                "xno", parsedFieldList.get(0).getAlias());
        Assert.assertEquals("字段別名解析不正確",
                "xx3200", parsedFieldList.get(3).getAlias());

        sql = "select cast(a as string) as b from ccc;";
        parsedFieldList = SimpleSqlFieldParser.parse(sql);
        System.out.println("result:");
        parsedFieldList.forEach(System.out::println);
        Assert.assertEquals("字段個(gè)數(shù)解析不正確",
                1, parsedFieldList.size());
        Assert.assertEquals("字段別名解析不正確",
                "b", parsedFieldList.get(0).getAlias());

        sql = "with a as(select cus,x1 from b1), b as (select cus,x2 from b2)\n" +
                "    select a.cus as a_cus, cast(a \nas string) as a_cus2, " +
                "b.x2 b2 from a join b on a.cus=b.cus where xxx;";
        parsedFieldList = SimpleSqlFieldParser.parse(sql);
        System.out.println("result:");
        parsedFieldList.forEach(System.out::println);
        Assert.assertEquals("字段個(gè)數(shù)解析不正確",
                3, parsedFieldList.size());
        Assert.assertEquals("字段別名解析不正確",
                "a_cus", parsedFieldList.get(0).getAlias());
        Assert.assertEquals("字段別名解析不正確",
                "b2", parsedFieldList.get(2).getAlias());

        sql = "select a.xno,b.xx,qqq from a_tb as a join b_tb as b on a.id = b.id";
        parsedFieldList = SimpleSqlFieldParser.parse(sql);
        System.out.println("result:");
        parsedFieldList.forEach(System.out::println);
        Assert.assertEquals("字段個(gè)數(shù)解析不正確",
                3, parsedFieldList.size());
        Assert.assertEquals("字段別名解析不正確",
                "xno", parsedFieldList.get(0).getAlias());
        Assert.assertEquals("字段別名解析不正確",
                "qqq", parsedFieldList.get(2).getAlias());

        sql = "select cast (a.a_int as string) a_str, b.xx, coalesce  \n( a, b, c) qqq from a_tb as a join b_tb as b on a.id = b.id";
        parsedFieldList = SimpleSqlFieldParser.parse(sql);
        System.out.println("result:");
        parsedFieldList.forEach(System.out::println);
        Assert.assertEquals("字段個(gè)數(shù)解析不正確",
                3, parsedFieldList.size());
        Assert.assertEquals("字段別名解析不正確",
                "a_str", parsedFieldList.get(0).getAlias());
        Assert.assertEquals("字段原始名解析不正確",
                "cast (a.a_int as string)", parsedFieldList.get(0).getFieldName());
        Assert.assertEquals("字段別名解析不正確",
                "qqq", parsedFieldList.get(2).getAlias());
        Assert.assertEquals("字段原始名解析不正確",
                "coalesce  \n( a, b, c)", parsedFieldList.get(2).getFieldName());
    }
}

至此,一個(gè)簡單的字段解析器完成。小工具,供參考!

到此這篇關(guān)于sql字段解析器的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)sql字段解析器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:
  • MYSQL替換時(shí)間(年月日)字段時(shí)分秒不變實(shí)例解析
  • 解析mysql不重復(fù)字段值求和
  • 解析如何用SQL語句在指定字段前面插入新的字段

標(biāo)簽:安徽 臺(tái)州 武威 濟(jì)寧 濟(jì)源 廣東 泰安 汕頭

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《sql字段解析器的實(shí)現(xiàn)示例》,本文關(guān)鍵詞  sql,字段,解析,器,的,實(shí)現(xiàn),;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《sql字段解析器的實(shí)現(xiàn)示例》相關(guān)的同類信息!
  • 本頁收集關(guān)于sql字段解析器的實(shí)現(xiàn)示例的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    校园春色亚洲色图_亚洲视频分类_中文字幕精品一区二区精品_麻豆一区区三区四区产品精品蜜桃
    国产精品99久久久久久宅男| 337p粉嫩大胆噜噜噜噜噜91av| 91在线精品一区二区| 国产激情视频一区二区在线观看| 久久99精品国产麻豆不卡| 久久av老司机精品网站导航| 日韩制服丝袜先锋影音| 免费一级片91| 国产在线精品视频| 国产精品99久久久久久久女警 | 国产一二精品视频| 国产在线视视频有精品| 欧美日韩一区不卡| 在线不卡的av| 日韩精品一区二区三区视频| 欧美一级理论片| 2024国产精品| 中文字幕乱码久久午夜不卡 | 国产精品久久精品日日| 亚洲色欲色欲www| 1024国产精品| 亚洲高清视频在线| 免费成人av资源网| 国产一区二区日韩精品| 国产乱一区二区| 99久久er热在这里只有精品15| 日本韩国精品在线| 欧美一区二区三区思思人| 日韩女优电影在线观看| 欧美国产综合色视频| 亚洲乱码国产乱码精品精98午夜 | 国产乱码精品一品二品| 成人av电影观看| 欧美吻胸吃奶大尺度电影| 日韩一级二级三级精品视频| 国产亚洲精品精华液| 亚洲美女视频一区| 强制捆绑调教一区二区| 成人免费的视频| 欧美三级午夜理伦三级中视频| 欧美一级高清大全免费观看| 日本一区二区三区在线观看| 亚洲一区视频在线| 国产一区二区成人久久免费影院| 97se亚洲国产综合自在线不卡| 欧美美女直播网站| 欧美国产激情二区三区| 亚洲国产va精品久久久不卡综合| 久久66热偷产精品| 91丝袜国产在线播放| 日韩欧美美女一区二区三区| 亚洲欧美综合网| 麻豆91在线播放| 99这里只有精品| 日韩三级在线免费观看| 国产精品国产三级国产| 麻豆高清免费国产一区| 色狠狠综合天天综合综合| 久久女同互慰一区二区三区| 亚洲一区二区三区影院| 丁香一区二区三区| 欧美一区二区三区不卡| 亚洲免费在线电影| 国产精品一二三四五| 欧美日韩午夜精品| 国产精品乱子久久久久| 紧缚捆绑精品一区二区| 欧美视频自拍偷拍| 中文字幕一区二区三区不卡在线 | 一区二区三区高清| 国产精品一卡二| 欧美一区欧美二区| 亚洲乱码国产乱码精品精98午夜| 国产真实乱子伦精品视频| 欧美精品自拍偷拍| 亚洲精品日韩专区silk| 国产成人精品三级| 精品国产污网站| 午夜日韩在线电影| 91老师国产黑色丝袜在线| 久久香蕉国产线看观看99| 男人操女人的视频在线观看欧美| 欧美综合在线视频| 1000精品久久久久久久久| 国产精品自拍在线| 日韩精品一区二区三区三区免费| 婷婷综合在线观看| 精品视频一区二区不卡| 亚洲精品视频观看| 91丝袜呻吟高潮美腿白嫩在线观看| 久久综合999| 久久精品国产久精国产| 91精品国产综合久久香蕉的特点| 一区二区三区在线观看欧美| 丁香六月综合激情| 国产精品网站在线播放| 国产一区高清在线| 久久久蜜臀国产一区二区| 黄色精品一二区| 精品国产乱码久久久久久老虎| 视频一区二区国产| 欧美日韩aaa| 天堂蜜桃一区二区三区| 欧美日韩国产综合视频在线观看| 亚洲在线视频网站| 欧美中文字幕一区二区三区亚洲| 亚洲精品久久久久久国产精华液 | 国产精品久久二区二区| 成人黄色软件下载| 国产精品高潮呻吟久久| 不卡一区二区中文字幕| 1区2区3区精品视频| 91在线视频免费91| 亚洲特黄一级片| 在线国产亚洲欧美| 亚洲一区二区精品久久av| 欧美影视一区在线| 日韩国产欧美一区二区三区| 日韩一区和二区| 国产电影一区在线| 成人免费视频在线观看| 一本大道久久a久久精二百| 夜色激情一区二区| 欧美精品少妇一区二区三区| 日本视频一区二区三区| 26uuuu精品一区二区| 成人综合在线网站| 亚洲视频一区二区免费在线观看| 欧美亚洲综合久久| 久久av老司机精品网站导航| 国产精品视频第一区| 91蜜桃免费观看视频| 日韩国产在线一| 精品国产伦一区二区三区免费 | 色综合网站在线| 五月天激情小说综合| 亚洲成av人片| 精品国产乱码久久久久久久久 | 欧美精品黑人性xxxx| 久久超级碰视频| 成人欧美一区二区三区白人| 欧美美女bb生活片| 国产成人午夜高潮毛片| 亚洲综合小说图片| 欧美一区二区三区视频| 成人丝袜高跟foot| 亚洲国产日产av| 精品99一区二区| 色综合天天综合网国产成人综合天 | 一个色综合网站| 在线综合+亚洲+欧美中文字幕| 国产成人小视频| 偷窥少妇高潮呻吟av久久免费| 国产欧美综合色| 欧美日韩国产一级二级| 成人黄色小视频在线观看| 亚洲成人av电影在线| 中文字幕国产精品一区二区| 91精品国产美女浴室洗澡无遮挡| 懂色av一区二区夜夜嗨| 日本va欧美va精品| 亚洲裸体在线观看| 久久精品日产第一区二区三区高清版 | 欧美日本精品一区二区三区| 成人免费毛片嘿嘿连载视频| 日韩激情一二三区| 亚洲欧美偷拍另类a∨色屁股| 欧美不卡一区二区三区| 欧美性xxxxxx少妇| 不卡的av电影在线观看| 久久av资源网| 爽好多水快深点欧美视频| 亚洲日本va午夜在线影院| 精品国产三级电影在线观看| 欧美在线视频你懂得| 成人动漫中文字幕| 激情六月婷婷久久| 天天影视涩香欲综合网 | 欧美偷拍一区二区| 国产成人午夜精品影院观看视频 | 91麻豆精品91久久久久久清纯 | 亚洲欧洲日韩av| 国产亚洲一区二区三区在线观看| 欧美电影一区二区| 色狠狠一区二区| 99re热视频精品| 成人综合婷婷国产精品久久蜜臀 | 久久伊99综合婷婷久久伊| 欧美日韩国产中文| 色综合色狠狠天天综合色| 成人白浆超碰人人人人| 国产不卡视频一区| 国产精品一品二品| 国产精品影音先锋| 久久精品久久久精品美女| 亚洲午夜成aⅴ人片| 亚洲日本欧美天堂| ●精品国产综合乱码久久久久| 国产亚洲欧美在线| 久久精品视频网|