最近要实现一个简易的数据库系统,除了要考虑如何高效的存储和访问数据,建立表关系外,对基本的sql查询语句要做一个解析,这样我们才能知道用户的查询要求;因为时间关系,参考了已有的一篇文章,并对其实现中出的小问题给予更正,在这里跟大家共享一下。原文请查阅http://www.cnblogs.com/pelephone/articles/sql-parse-single-word.html
第一步:先对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赵朝峰
*
*@since2013-6-10
*@version1.00*/
public classSqlParserUtil{/** *//*** 方法的主要入口
*@paramsql:要解析的sql语句
*@return返回解析结果*/
publicString getParsedSql(String sql){
sql=sql.trim();
sql=sql.toLowerCase();
sql=sql.replaceAll("\\s{1,}", " ");
sql=""+sql+" ENDOFSQL";//System.out.println(sql);
returnSingleSqlParserFactory.generateParser(sql).getParsedSql();
}/** *//*** SQL语句解析的接口
*@paramsql:要解析的sql语句
*@return返回解析结果*/
public ListgetParsedSqlList(String sql)
{
sql=sql.trim();
sql=sql.toLowerCase();
sql=sql.replaceAll("\\s{1,}", " ");
sql=""+sql+" ENDOFSQL";//System.out.println(sql);
returnSingleSqlParserFactory.generateParser(sql).RetrunSqlSegments();
}
}
packagecom.sitinspring.common.sqlparser.single;//import com.sitinspring.common.sqlparser.single.NoSqlParserException;
importjava.util.ArrayList;importjava.util.List;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//*** 单句Sql解析器,单句即非嵌套的意思
*@author赵朝峰()
*
*@since2013-6-10
*@version1.00*/
public abstract classBaseSingleSqlParser{/** *//*** 原始Sql语句*/
protectedString originalSql;/** *//*** Sql语句片段*/
protected Listsegments;/** *//*** 构造函数,传入原始Sql语句,进行劈分。
*@paramoriginalSql*/
publicBaseSingleSqlParser(String originalSql){this.originalSql=originalSql;
segments=new ArrayList();
initializeSegments();
splitSql2Segment();
}/** *//*** 初始化segments,强制子类实现
**/
protected abstract voidinitializeSegments();/** *//*** 将originalSql劈分成一个个片段
**/
protected voidsplitSql2Segment() {for(SqlSegment sqlSegment:segments)
{
sqlSegment.parse(originalSql);
}
}/** *//*** 得到解析完毕的Sql语句
*@return
*/
publicString getParsedSql() {//测试输出各个片段的信息
/*for(SqlSegment sqlSegment:segments)
{
String start=sqlSegment.getStart();
String end=sqlSegment.getEnd();
System.out.println(start);
System.out.println(end);
}*/StringBuffer sb=newStringBuffer();for(SqlSegment sqlSegment:segments)
{
sb.append(sqlSegment.getParsedSqlSegment());
}
String retval=sb.toString().replaceAll("@+", "\n");returnretval;
}/** *//*** 得到解析的Sql片段
*@return
*/
public ListRetrunSqlSegments()
{int SegmentLength=this.segments.size();if(SegmentLength!=0)
{
List result=this.segments;returnresult;
}else{//throw new Exception();
return null;
}
}
}
packagecom.sitinspring.common.sqlparser.single;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//***
* 单句删除语句解析器
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public class DeleteSqlParser extendsBaseSingleSqlParser{publicDeleteSqlParser(String originalSql) {super(originalSql);
}
@Overrideprotected voidinitializeSegments() {
segments.add(new SqlSegment("(delete from)(.+)( where | ENDOFSQL)","[,]"));
segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));
}
}
packagecom.sitinspring.common.sqlparser.single;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//***
* 单句查询插入语句解析器
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public class InsertSelectSqlParser extendsBaseSingleSqlParser{publicInsertSelectSqlParser(String originalSql) {super(originalSql);
}
@Overrideprotected voidinitializeSegments() {
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)","[,]"));
}
}
packagecom.sitinspring.common.sqlparser.single;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//***
* 单句插入语句解析器
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public class InsertSqlParser extendsBaseSingleSqlParser{publicInsertSqlParser(String originalSql) {super(originalSql);
}
@Overrideprotected voidinitializeSegments() {
segments.add(new SqlSegment("(insert into)(.+)([(])","[,]"));
segments.add(new SqlSegment("([(])(.+)( [)] values )","[,]"));
segments.add(new SqlSegment("([)] values [(])(.+)( [)])","[,]"));
}
@OverridepublicString getParsedSql() {
String retval=super.getParsedSql();
retval=retval+")";returnretval;
}
}
packagecom.sitinspring.common.sqlparser.single;public class NoSqlParserException extendsException{private static final long serialVersionUID = 1L;
NoSqlParserException()
{
}
NoSqlParserException(String sql)
{//调用父类方法
super(sql);
}
}
packagecom.sitinspring.common.sqlparser.single;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//***
* 单句查询语句解析器
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public class SelectSqlParser extendsBaseSingleSqlParser{publicSelectSqlParser(String originalSql) {super(originalSql);
}
@Overrideprotected voidinitializeSegments() {
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)","[,]"));
}
}
packagecom.sitinspring.common.sqlparser.single;importjava.util.regex.Matcher;importjava.util.regex.Pattern;//import com.sitinspring.common.sqlparser.single.NoSqlParserException;/** *//*** 单句Sql解析器制造工厂
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public classSingleSqlParserFactory{public staticBaseSingleSqlParser generateParser(String sql)
{if(contains(sql,"(insert into)(.+)(select)(.+)(from)(.+)"))
{return newInsertSelectSqlParser(sql);
}else if(contains(sql,"(select)(.+)(from)(.+)"))
{return newSelectSqlParser(sql);
}else if(contains(sql,"(delete from)(.+)"))
{return newDeleteSqlParser(sql);
}else if(contains(sql,"(update)(.+)(set)(.+)"))
{return newUpdateSqlParser(sql);
}else if(contains(sql,"(insert into)(.+)(values)(.+)"))
{return newInsertSqlParser(sql);
}//sql=sql.replaceAll("ENDSQL", "");
else
return newInsertSqlParser(sql);//throw new NoSqlParserException(sql.replaceAll("ENDOFSQL", ""));//对异常的抛出
}/** *//*** 看word是否在lineText中存在,支持正则表达式
*@paramsql:要解析的sql语句
*@paramregExp:正则表达式
*@return
*/
private static booleancontains(String sql,String regExp){
Pattern pattern=Pattern.compile(regExp,Pattern.CASE_INSENSITIVE);
Matcher matcher=pattern.matcher(sql);returnmatcher.find();
}
}
packagecom.sitinspring.common.sqlparser.single;importjava.util.ArrayList;importjava.util.List;importjava.util.regex.Matcher;importjava.util.regex.Pattern;/** *//*** Sql语句片段
*
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public classSqlSegment{private static final String Crlf = "@";private static final String FourSpace = " ";/** *//*** Sql语句片段开头部分*/
privateString start;/** *//*** Sql语句片段中间部分*/
privateString body;/** *//*** Sql语句片段结束部分*/
privateString end;/** *//*** 用于分割中间部分的正则表达式*/
privateString bodySplitPattern;/** *//*** 表示片段的正则表达式*/
privateString segmentRegExp;/** *//*** 分割后的Body小片段*/
private ListbodyPieces;/** *//*** 构造函数
*@paramsegmentRegExp 表示这个Sql片段的正则表达式
*@parambodySplitPattern 用于分割body的正则表达式*/
publicSqlSegment(String segmentRegExp,String bodySplitPattern){
start="";
body="";
end="";this.segmentRegExp=segmentRegExp;this.bodySplitPattern=bodySplitPattern;this.bodyPieces=new ArrayList();
}/** *//*** 从sql中查找符合segmentRegExp的部分,并赋值到start,body,end等三个属性中
*@paramsql*/
public voidparse(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 voidparseBody(){
List ls=new ArrayList();
Pattern p=Pattern.compile(bodySplitPattern,Pattern.CASE_INSENSITIVE);//先清除掉前后空格
body=body.trim();
Matcher m=p.matcher(body);
StringBuffer sb= newStringBuffer();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
{
String temp=FourSpace+arr[i];if(i!=arrLength-1)
{//temp=temp+Crlf;
}
ls.add(temp);
}
bodyPieces=ls;
}/** *//*** 取得解析好的Sql片段
*@return
*/
publicString getParsedSqlSegment(){
StringBuffer sb=newStringBuffer();
sb.append(start+Crlf);for(String piece:bodyPieces)
{
sb.append(piece+Crlf);
}returnsb.toString();
}publicString getBody()
{returnbody;
}public voidsetBody(String body)
{this.body=body;
}publicString getEnd()
{returnend;
}public voidsetEnd(String end)
{this.end=end;
}publicString getStart()
{returnstart;
}public voidsetStart(String start)
{this.start=start;
}
}
packagecom.sitinspring.common.sqlparser.single;importcom.sitinspring.common.sqlparser.single.SqlSegment;/** *//***
* 单句更新语句解析器
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public class UpdateSqlParser extendsBaseSingleSqlParser{publicUpdateSqlParser(String originalSql) {super(originalSql);
}
@Overrideprotected voidinitializeSegments() {
segments.add(new SqlSegment("(update)(.+)(set)","[,]"));
segments.add(new SqlSegment("(set)(.+)( where | ENDOFSQL)","[,]"));
segments.add(new SqlSegment("(where)(.+)( ENDOFSQL)","(and|or)"));
}
}
执行结果:自己写了个测试的类
importjava.util.List;import com.sitinspring.common.sqlparser.single.*;public classTest {/** *//*** 单句Sql解析器制造工厂
*@author赵朝峰
*
*@since2013-6-10
*@version1.00*/
public static voidmain(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=newSqlParserUtil();
String result=test.getParsedSql(testSql);
System.out.println(result);//List result=test.getParsedSqlList(testSql);//保存解析结果
}
}
结果为
select
c1,
c2,
c3
from
t1,
t2
where
condi3=3
or
condi4=5
order by
o1,
o2