`

sql语句解析

阅读更多

 

第一步:先对sql语句进行预处理;

对于用户,我们应该接受各种形式的查询语句书写,单行或者多行,语句中单个空格或者多个空格的间隔等等。但是我们要解析sql语句,就首先要让对它们做标准化,这样才能进行我们下一步处理。系统中的处理要求:

1)消除SQL语句前后的空白,将其中的连续空白字符(包括空格,TAB和回车换行)替换成单个空格;

2)将sql语句全变成小写形式(或大写形式);

3)在SQL语句的尾后加上结束符号“ENDOFSQL”(原因后面解释)

例如:用户输入:“select c1,c2,c3 from  t1,t2, t3 where condi1=5 and condi6=6 or condi7=7 order 

by g1,g2”

通过预处理应该为:“select c1,c2,c3 from t1,t2,t3 where condi1=5 and condi6=6 or condi7=7 order by g1,g2”

第二步:将查询语句切分为语句块;

以查询语句为例(本文中主要是以查询语句作为例子讲解,其它删除,插入等语句原理于此类似,因为查询语句相对复杂,所以用来i讲解),正如上面我们标准化后的语句一样,我们要进行下一步的,对表中数据的处理,首先要知道是对那些表处理,要满足那些条件,输出那些属性,以什么顺序输出等。所以查询语句就可以分割为以下几个块:

1)select c1,c2,c3 from:属性输出块;块头(start)select,块尾(end)from,这个块我们关心的信息(body):c1,c2,c3;以下块类似分析

2)from....where; 涉及数据表块。

3)where.....order by; 查询条件块。

4)order by.....ENDOFSQL; 属性输出顺序。这里也就看出了我们为什么要在查询语句末尾加上结束符,是为了最后一个块的限定需要。

知道了如何分块,接下来要做的就是在我们已经标准化的sql语句上进行块的切割,这里我们用到了正则表达式,以第二个块from....where的查询为例,我们做个分析

"(from)(.+)( where | on | having | group by | order by | ENDOFSQL)“

以上就是第二个块的正则匹配式(其它块的匹配式下面也会给出),可以看出,在一个sql查询语句中,from块中跟from搭配出现的不只是where,还可以是on,having,group by等,那么通过这个正则式我们可以得到如下的块:

      from .... where

  from .... on

  from .... having

  from .... group by

  from .... order by

  from .... ENDOFSQL

这里我们要注意一点,就是在通过正则式对sql语句进行匹配时,我们不能对整个sql语句进行一次匹配操作,因为正则匹配是贪心匹配,它总是尽可能的向后查找,匹配到最大的语句段。就拿上述语句为例,如果通过对整个sql语句进行一次匹配,得到的就不是from....where这个语句段而是from .... where .... order by。显然这不是我们想要的。所以我们只能牺牲效率,通过对整个sql语句进行逐次递增的查询方式来查找相应的语句块。给出一个查询过程,加强理解,以上述sql语句为例,对第一个语句块的查找过程是

s
se
sel
sele
selec
select
select 
select c
select c1
select c1,
select c1,c
select c1,c2
select c1,c2,
select c1,c2,c
select c1,c2,c3
select c1,c2,c3 
select c1,c2,c3 f
select c1,c2,c3 fr
select c1,c2,c3 fro
select c1,c2,c3 from

这样就找到第一个块,以此类推,找第二个块时候又从头逐次递增查找。

第三步:找到了各个块,我们还要把我们最关心的信息提取出来,就是夹在语句块头和尾之间的body部分,这个就好实现了,一般的sql语句中都会用逗号来做分割,我们提取出各个块的body信息。

步骤介绍完了,下面就上代码,享乐吧...少年!

复制代码
package com.sitinspring.common.sqlparser.single;

import java.util.List;

/** *//**
* 单句Sql解析器制造工厂
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class SqlParserUtil{
    /** *//**
     * 方法的主要入口
     * @param sql:要解析的sql语句
     * @return 返回解析结果
     */
    public String getParsedSql(String sql){
        sql=sql.trim();
        sql=sql.toLowerCase();
        sql=sql.replaceAll("\\s{1,}", " ");
        sql=""+sql+" ENDOFSQL";
        //System.out.println(sql);
        return SingleSqlParserFactory.generateParser(sql).getParsedSql();
    }
    
    /** *//**
     * SQL语句解析的接口
     * @param sql:要解析的sql语句
     * @return 返回解析结果
     */
    public List<SqlSegment> getParsedSqlList(String sql)
    {
        sql=sql.trim();
        sql=sql.toLowerCase();
        sql=sql.replaceAll("\\s{1,}", " ");
        sql=""+sql+" ENDOFSQL";
        //System.out.println(sql);
        return SingleSqlParserFactory.generateParser(sql).RetrunSqlSegments();
    }
    }
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

//import com.sitinspring.common.sqlparser.single.NoSqlParserException;
import java.util.ArrayList;
import java.util.List;
import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
* 单句Sql解析器,单句即非嵌套的意思
* @author 赵朝峰()
*
* @since 2013-6-10
* @version 1.00
*/
public abstract class BaseSingleSqlParser{
/** *//**
 * 原始Sql语句
 */
protected String originalSql;
/** *//**
 * Sql语句片段
 */
protected List<SqlSegment> segments;
/** *//**
 * 构造函数,传入原始Sql语句,进行劈分。
 * @param originalSql
 */
public BaseSingleSqlParser(String originalSql){
    this.originalSql=originalSql;
    segments=new ArrayList<SqlSegment>();
    initializeSegments();
    splitSql2Segment();
}
/** *//**
 * 初始化segments,强制子类实现
 *
 */
protected abstract void initializeSegments();
/** *//**
 * 将originalSql劈分成一个个片段
 *
 */
protected void splitSql2Segment() {
    for(SqlSegment sqlSegment:segments)
    {
        sqlSegment.parse(originalSql);
    }
}
/** *//**
 * 得到解析完毕的Sql语句
 * @return
 */
public String getParsedSql() {
    
    //测试输出各个片段的信息
    /*
    for(SqlSegment sqlSegment:segments)
    {
        String start=sqlSegment.getStart();
        String end=sqlSegment.getEnd();
        System.out.println(start);
        System.out.println(end);
    }
    */
    
    StringBuffer sb=new StringBuffer();
    for(SqlSegment sqlSegment:segments)
    {
        sb.append(sqlSegment.getParsedSqlSegment());
    }
    String retval=sb.toString().replaceAll("@+", "\n");
    return retval;
}
/** *//**
* 得到解析的Sql片段
* @return
*/
public List<SqlSegment> RetrunSqlSegments()
{
    int SegmentLength=this.segments.size();
    if(SegmentLength!=0)
    {
      List<SqlSegment> result=this.segments;
      return result;
    }
    else
    {
        //throw new Exception();
        return null;
    }
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
*
* 单句删除语句解析器
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class DeleteSqlParser extends BaseSingleSqlParser{
public DeleteSqlParser(String originalSql) {
    super(originalSql);
}
@Override
protected void initializeSegments() {
    segments.add(new SqlSegment("(delete from)(.+)( where | ENDOFSQL)","[,]"));
    segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
*
* 单句查询插入语句解析器
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class InsertSelectSqlParser extends BaseSingleSqlParser{
public InsertSelectSqlParser(String originalSql) {
    super(originalSql);
}
@Override
protected void initializeSegments() {
    segments.add(new SqlSegment("(insert into)(.+)( select )","[,]"));
    segments.add(new SqlSegment("(select)(.+)(from)","[,]"));
    segments.add(new SqlSegment("(from)(.+)( where | on | having | groups+by | orders+by | ENDOFSQL)","(,|s+lefts+joins+|s+rights+joins+|s+inners+joins+)"));
    segments.add(new SqlSegment("(where|on|having)(.+)( groups+by | orders+by | ENDOFSQL)","(and|or)"));
    segments.add(new SqlSegment("(groups+by)(.+)( orders+by| ENDOFSQL)","[,]"));
    segments.add(new SqlSegment("(orders+by)(.+)( ENDOFSQL)","[,]"));
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
*
* 单句插入语句解析器
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class InsertSqlParser extends BaseSingleSqlParser{
public InsertSqlParser(String originalSql) {
    super(originalSql);
}
@Override
protected void initializeSegments() {
    segments.add(new SqlSegment("(insert into)(.+)([(])","[,]"));
    segments.add(new SqlSegment("([(])(.+)( [)] values )","[,]"));
    segments.add(new SqlSegment("([)] values [(])(.+)( [)])","[,]"));
}
@Override
public String getParsedSql() {
    String retval=super.getParsedSql();
    retval=retval+")";
    return retval;
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

public class NoSqlParserException extends Exception{
    private static final long serialVersionUID = 1L;
    NoSqlParserException()
    {
        
    }
    NoSqlParserException(String sql)
    {
        //调用父类方法
        super(sql);
    }
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
*
* 单句查询语句解析器
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class SelectSqlParser extends BaseSingleSqlParser{
public SelectSqlParser(String originalSql) {
    super(originalSql);
}
@Override
protected void initializeSegments() {
    segments.add(new SqlSegment("(select)(.+)(from)","[,]"));
    segments.add(new SqlSegment("(from)(.+)( where | on | having | group by | order by | ENDOFSQL)","(,| left join | right join | inner join )"));
    segments.add(new SqlSegment("(where|on|having)(.+)( group by | order by | ENDOFSQL)","(and|or)"));
    segments.add(new SqlSegment("(group by)(.+)( order by| ENDOFSQL)","[,]"));
    segments.add(new SqlSegment("(order by)(.+)( ENDOFSQL)","[,]"));
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import com.sitinspring.common.sqlparser.single.NoSqlParserException;
/** *//**
* 单句Sql解析器制造工厂
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class SingleSqlParserFactory{
public static BaseSingleSqlParser generateParser(String sql)
{
    if(contains(sql,"(insert into)(.+)(select)(.+)(from)(.+)"))
    {
        return new InsertSelectSqlParser(sql);
    }
    else if(contains(sql,"(select)(.+)(from)(.+)"))
    {
        return new SelectSqlParser(sql);
    }
    else if(contains(sql,"(delete from)(.+)"))
    {
        return new DeleteSqlParser(sql);
    }
    else if(contains(sql,"(update)(.+)(set)(.+)"))
    {
        return new UpdateSqlParser(sql);
    }
    else if(contains(sql,"(insert into)(.+)(values)(.+)"))
    {
        return new InsertSqlParser(sql);
    }
    //sql=sql.replaceAll("ENDSQL", "");
    else
        return new InsertSqlParser(sql);
       //throw new NoSqlParserException(sql.replaceAll("ENDOFSQL", ""));//对异常的抛出
}
/** *//**
 * 看word是否在lineText中存在,支持正则表达式
 * @param sql:要解析的sql语句
 * @param regExp:正则表达式
 * @return
 */
private static boolean contains(String sql,String regExp){
    Pattern pattern=Pattern.compile(regExp,Pattern.CASE_INSENSITIVE);
    Matcher matcher=pattern.matcher(sql);
    return matcher.find();
}
}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** *//**
* Sql语句片段
*
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class SqlSegment{
private static final String Crlf = "@";
private static final String FourSpace = "  ";
/** *//**
 * Sql语句片段开头部分
 */
private String start;
/** *//**
 * Sql语句片段中间部分
 */
private String body;
/** *//**
 * Sql语句片段结束部分
 */
private String end;
/** *//**
 * 用于分割中间部分的正则表达式
 */
private String bodySplitPattern;
/** *//**
 * 表示片段的正则表达式
 */
private String segmentRegExp;
/** *//**
 * 分割后的Body小片段
 */
private List<String> bodyPieces;
/** *//**
 * 构造函数
 * @param segmentRegExp 表示这个Sql片段的正则表达式
 * @param bodySplitPattern 用于分割body的正则表达式
 */
public SqlSegment(String segmentRegExp,String bodySplitPattern){
    start="";
    body="";
    end="";
    this.segmentRegExp=segmentRegExp;
    this.bodySplitPattern=bodySplitPattern;
    this.bodyPieces=new ArrayList<String>();
    
}
/** *//**
 * 从sql中查找符合segmentRegExp的部分,并赋值到start,body,end等三个属性中
 * @param sql
 */
public void parse(String sql){
    Pattern pattern=Pattern.compile(segmentRegExp,Pattern.CASE_INSENSITIVE);
    for(int i=0;i<=sql.length();i++)
    {
     String shortSql=sql.substring(0, i);
     //测试输出的子句是否正确
     System.out.println(shortSql);
     Matcher matcher=pattern.matcher(shortSql);
     while(matcher.find())
     {
         start=matcher.group(1);
         body=matcher.group(2);
         //测试body部分
         //System.out.println(body);
         end=matcher.group(3);
         //测试相应的end部分
         //System.out.println(end);
         parseBody();
         return;
     }
    }
}
/** *//**
 * 解析body部分
 *
 */
private void parseBody(){
    
    List<String> ls=new ArrayList<String>();
    Pattern p = Pattern.compile(bodySplitPattern,Pattern.CASE_INSENSITIVE);
    // 先清除掉前后空格
    body=body.trim();
    Matcher m = p.matcher(body);
    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(" ");
    int arrLength=arr.length;
    for(int i=0;i<arrLength;i++)
    {
        String temp=FourSpace+arr[i];
        if(i!=arrLength-1)
        {
            //temp=temp+Crlf;
        }
        ls.add(temp);
    }
    bodyPieces=ls;
}
/** *//**
 * 取得解析好的Sql片段
 * @return
 */
public String getParsedSqlSegment(){
    StringBuffer sb=new StringBuffer();
    sb.append(start+Crlf);
    for(String piece:bodyPieces)
    {
        sb.append(piece+Crlf);
    }
    return sb.toString();
}

public String getBody()
{
    return body;
}

public void setBody(String body)
{
    this.body=body;
}

public String getEnd()
{
    return end;
}

public void setEnd(String end)
{
    this.end=end;
}

public String getStart()
{
    return start;
}


public void setStart(String start) 
{
    this.start=start;
}


}
复制代码
复制代码
package com.sitinspring.common.sqlparser.single;

import com.sitinspring.common.sqlparser.single.SqlSegment;
/** *//**
*
* 单句更新语句解析器
* @author 赵朝峰
*
* @since 2013-6-10
* @version 1.00
*/
public class UpdateSqlParser extends BaseSingleSqlParser{
public UpdateSqlParser(String originalSql) {
    super(originalSql);
}
@Override
protected void initializeSegments() {
    segments.add(new SqlSegment("(update)(.+)(set)","[,]"));
    segments.add(new SqlSegment("(set)(.+)( where | ENDOFSQL)","[,]"));
    segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));
}
}
复制代码

执行结果:自己写了个测试的类

复制代码
import java.util.List;

import com.sitinspring.common.sqlparser.single.*;
public class Test {
    /** *//**
    * 单句Sql解析器制造工厂
    * @author 赵朝峰
    *
    * @since 2013-6-10
    * @version 1.00
    */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
       //String test="select  a from  b " +
           //    "\n"+"where      a=b";
       //test=test.replaceAll("\\s{1,}", " ");
       //System.out.println(test);
       //程序的入口
        String testSql="select c1,c2,c3     from    t1,t2 where condi3=3 "+"\n"+"    or condi4=5 order by o1,o2";
        SqlParserUtil test=new SqlParserUtil();
        String result=test.getParsedSql(testSql);
        System.out.println(result);
       //List<SqlSegment> result=test.getParsedSqlList(testSql);//保存解析结果
    }

}
复制代码

结果为

select
     c1,
     c2,
     c3
from
      t1,
      t2
where
  condi3=3
  or
  condi4=5
order by
  o1,
     o2

 

转自:http://www.cnblogs.com/zcftech/archive/2013/06/10/3131286.html

分享到:
评论

相关推荐

    JsonSQL:用SQL语句解析JSON文件

    **JsonSQL: SQL语句解析JSON文件** 在大数据处理和Web应用中,JSON(JavaScript Object Notation)格式已经成为数据交换的常见格式。然而,对于习惯使用SQL查询关系型数据库的人来说,处理JSON数据可能会觉得不太...

    通用SQL语句解析类SqlParser(JAVA版)

    项目中已使用的一个比较简单的SQL语句查询字段解析工具类

    基于Python的sqlparse库SQL语句解析设计源码

    该项目是采用Python编写的基于sqlparse库的SQL语句解析设计源码,共计46个文件,涵盖32个Python源文件、5个配置文件(YAML格式)、1个代码风格检查配置文件、1个GitHub代码所有者配置文件、1个Git忽略规则文件、1个...

    通过解析sql语句获取表血缘关系项目

    1. **SQL语句解析**:SQL(Structured Query Language)是用于管理和处理关系型数据库的标准语言。解析SQL语句是指将人类可读的SQL命令转化为计算机可执行的指令。这一过程通常由数据库管理系统(DBMS)完成,但开发者...

    SQL 语句解析适用于初学者

    本教程面向初学者,旨在提供全面的SQL基础知识,帮助读者快速掌握SQL语句的基本语法和用途。 一、SQL简介 SQL全称结构化查询语言,它允许用户对数据库进行创建、查询、更新和删除操作。SQL被广泛应用于各种数据库...

    数据库sql语句解析系统.pdf

    数据库SQL语句解析系统是一种专门用于分析和执行SQL语句的软件系统,它在数据库管理系统(DBMS)中扮演着至关重要的角色。SQL(Structured Query Language)即结构化查询语言,是用于管理关系型数据库的标准编程语言...

    面向数据库性能的SQL语句解析与翻译.pdf

    【SQL语句解析与翻译的重要性】 SQL(Structured Query Language)是用于管理和操作关系型数据库的标准语言,对于数据库性能优化和安全至关重要。SQL语句解析与翻译是数据库管理系统中的关键技术,它能够帮助非专业...

    java版本的SQL语句解析源代码,包含demo(zql)

    SQL语句解析java包,含源代码: SELECT ANTIQUEOWNERS.OWNERLASTNAME, ANTIQUEOWNERS.OWNERFIRSTNAME FROM ANTIQUEOWNERS, ANTIQUES WHERE ANTIQUES.BUYERID = ANTIQUEOWNERS.OWNERID AND ANTIQUES.ITEM = 'Chair'; ...

    sql语句解析.zip

    sql语句分析,主要用于dug模式下打印出来的sql分析,自动对sql语句进行解析,对查询条件、字段赋值进行准确的赋值,非常实用的一款工具。 操作非常简单: 1、copy打印出来的sql 2、点击sql分析,就得到你想要的...

    从mybatis日志中解析出可执行的sql语句

    适用场景:生产环境从sql日志获取可执行sql语句 使用方法:从日志中获取完整的sql片段(可以前后多复制一部分,确保准确性),打开html文件,粘贴到输入框中点击解析sql,获得可执行的sql

    node-sql-parser:使用访问的tableList将简单SQL语句解析为抽象语法树(AST),并将其转换回SQL

    Nodejs SQL解析器 使用访问的tableList,columnList将简单SQL语句解析为抽象语法树(AST),然后将其转换回SQL。 :star: 特征支持用分号分隔的多个sql语句支持选择,删除,更新和插入类型支持放置,截断和重命名命令...

    Mybatis日志SQL解析工具

    该工具可以将mybatis输出的sql日志提取出来,并将其格式化为可以直接执行的sql语句,节约开发人员时间

    SQL语句大全,开发手册

    #### SQL语句解析: 1. **连接MySQL服务器**: - `mysql -h主机地址 -u用户名 -p密码` 使用命令行方式连接MySQL数据库,其中`-h`后跟的是数据库服务器的地址,`-u`指定登录的用户名,`-p`后直接输入密码或按回车...

    Spark-Sql源码解析

    在 Spark-Sql 源码解析中,SQL 语句首先被传递给 SqlParser,SqlParser 负责将 SQL 语句解析成抽象语法树(AST)。然后,AST 被传递给 Analyzer,Analyzer 负责对 AST 进行语义分析和优化,生成逻辑计划。逻辑计划...

    SQL语句详解

    SQL语句详解

    oracle sql语句执行流程解析

    接下来,服务器进程会对SQL语句进行解析。解析是查询执行前的重要步骤,包括以下几个方面: 1. 查询高速缓存(library cache):服务器首先检查是否存在与提交的SQL语句相同的执行计划在高速缓存中。如果存在,可以...

    SQL.zip_python sql_python 解析sql_python的SQL解析_python解析sql_sql 解析

    这个库能够将SQL语句解析为一个抽象语法树(AST),方便进一步处理和分析。 - 另一个库是`pandasql`,它允许用户在Python环境中使用SQL查询DataFrame对象,类似于处理数据库表。 2. **Python与Excel的交互**: - ...

    sql 解析字符串为数组,根据索引,获得数组的值

    sql通用函数, 解析字符串为数组,根据索引,获得数组的值

    基于SQL 语法解释器JSqlParser实现的SQL解析工具

    1. SQL语句解析:工具能够接收一个SQL语句字符串,然后使用JSqlParser解析器将其转化为AST。这一步骤允许开发者获取SQL语句的结构信息,如SELECT、FROM、WHERE等部分。 2. AST遍历与操作:解析后的AST可以通过遍...

Global site tag (gtag.js) - Google Analytics