`
seya
  • 浏览: 355850 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java解析sql语句,分析出调用到的所有表

    博客分类:
  • J2EE
阅读更多
基本思想:
1。对简单的sql语句,类似select A.a, B.b from A, B left join C on b.b = C.c where ...
这种不包含子查询,没有union/minus进行关联的语句,只要根据关键词来分析就行了。先把他们join替换成‘,’以便统一分析。从而将问题简化为解析from关键词后面的table名字,多表的话,一定是用','隔开的,这样就好分析了。
2。对于包含子查询,union/minus来关联的语句,把他们先分解成两部分,除去子查询或者union/minus关联的那部分,属于简单sql语句,用方法1就行了。对于子查询或者union/minus关联的那部分,采用递归。最终还是化解到方法1莱解决。
3。对于子查询,需要递归到下一层,那么在当前这一层,用subTable来进行替代,以保证当前简单sql语句的完整性。最后统计table的时候,对于subTable就不算进去了。

详细见一下代码:
package com.sqlparser;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;

public class TableQuerier {
	public static String startKey = "SELECT ";
	public static String fromKey = " FROM ";
	public static String joinKey = " JOIN ";
	public static String onKey = " ON ";
	public static String endKey = " WHERE ";
	public static String unionKey = " UNION ";
	public static String minusKey = " MINUS ";
	public String subTable = "subTable";
	private HashSet tableSet;
	private String fileName;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TableQuerier tq = new TableQuerier("c://data.txt");
		HashSet s = tq.getTableSet();
		tq.parseTableFromFile();
		Iterator i = s.iterator();
		System.out.println("The following tables are included in this sql statement.");
		int num = 1;
		System.out.println("Number#     tableName");
		while(i.hasNext()){
			System.out.println(num+" "+i.next());
			num++;
		}
		
	}
	public void parseTableFromFile(){
		BufferedReader buf = null;
		try {
			buf = new BufferedReader(new FileReader(fileName));
			
			String line = buf.readLine();
			
			StringBuilder sb = new StringBuilder();
			
			while(line != null){
				sb.append(line.toUpperCase()+" ");
				line = buf.readLine();
			}
			//System.out.println(sb.toString());
			
			getLevelOneTables(sb.toString(), tableSet);
			
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				buf.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}
	
	public TableQuerier(String fileName){
		tableSet = new HashSet();
		this.fileName = fileName;
	}
	
	public void getLevelOneTables(String str, HashSet s){
		if("".equals(str)){
			return;
		}
		//remove sub query
		int firstLeftBlockPos = str.indexOf("(");
		int lastRightBlockPos = -1;
		
		while(firstLeftBlockPos != -1){
			//get matched ')', remove the substring between them
			int leftBlockNum = 1;
			for(int start = firstLeftBlockPos +1; start < str.length(); start++){
				if(str.charAt(start) == ')'){
					if(leftBlockNum == 1){
						lastRightBlockPos = start;
						break;
					}else{
						leftBlockNum --;
					}
				}else if(str.charAt(start) == '('){
					leftBlockNum ++;
				}
			}
			if(lastRightBlockPos < 0){
				try {
					throw new Exception("block mismatch");
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			String blockContent = str.substring(firstLeftBlockPos+1, lastRightBlockPos);
			
			
			//parse the content in block
			
			if(blockContent.contains(startKey)){
				getLevelOneTables(blockContent, s);
				str = str.substring(0,firstLeftBlockPos)+" "+subTable+" "+str.substring(lastRightBlockPos+1);
			}else{//remove '(' and ')' at level one
				str = str.substring(0,firstLeftBlockPos)
						+str.substring(firstLeftBlockPos+1,lastRightBlockPos)
				 		+str.substring(lastRightBlockPos +1);
			}
			firstLeftBlockPos = str.indexOf("(");
		}
		//if the statement is joined with 'UNION' or 'MINUS', 
		//parse the first segement left of the key word('UNION' or 'MINUS')
		int unionPos = -1;
		int minusPos = -1;
		int breakPos = -1;
		unionPos = str.indexOf(unionKey);
		minusPos = str.indexOf(minusKey);
		if(unionPos != -1 ||minusPos != -1){
			if(unionPos != -1 && minusPos != -1){
				breakPos = minusPos < unionPos ? minusPos:unionPos;
			}else{
				breakPos = minusPos < unionPos ? unionPos:minusPos;
			}
		}
		
		if(breakPos != -1){
			String rightContent = str.substring(breakPos+unionKey.length()).trim();
			rightContent = rightContent.startsWith("ALL")?rightContent.substring(3):rightContent;
			getLevelOneTables(rightContent, s);
			str = str.substring(0, breakPos).trim();
		}
		//change join to ','
        //		if join table
		int joinKeyPos = str.indexOf(joinKey);
		if(joinKeyPos != -1){
			str = str.replace(joinKey, ",");
		}
		
		//now, we can look for tables in a simple statement
		int fromPos = str.indexOf(fromKey);
		int endPos = str.indexOf(endKey);
		if(endPos == -1){
			endPos = str.length();
		}
		if(fromPos > 0){
			str = str.substring(fromPos+fromKey.length(), endPos).trim();
		}else{
			return;
		}
		//System.out.println("modified str  = "+str);
		String tableName = str.indexOf(" ") > 0?str.substring(0,str.indexOf(" ")).trim():str.trim();//the first word after 'from' is a table
		if(!subTable.equals(tableName)){
			s.add(tableName);
		}
		
		str = str.substring(str.indexOf(tableName)+tableName.length()).trim();
		//if multiple table after 'from'
		int dotIndex = str.indexOf(",");
		while(dotIndex != -1){
			str = str.substring(dotIndex+1).trim();
			tableName = str.indexOf(" ")>0?str.substring(0,str.indexOf(" ")).trim():str.trim();//the first word after ',' must be a table name
			if(!subTable.equals(tableName)){
				s.add(tableName);
			}
			str = str.substring(str.indexOf(tableName)+tableName.length()).trim();
			dotIndex = str.indexOf(",");
		}
	}
	
	
	/**
	 * @return the fileName
	 */
	public String getFileName() {
		return fileName;
	}
	/**
	 * @param fileName the fileName to set
	 */
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	/**
	 * @return the tableSet
	 */
	public HashSet getTableSet() {
		return tableSet;
	}
	/**
	 * @param tableSet the tableSet to set
	 */
	public void setTableSet(HashSet tableSet) {
		this.tableSet = tableSet;
	}
}


这是针对一条sql语句的解析方法。下次有时间可以改进成针对存储过程,函数的解析方法。

未经大量测试,如果有bug请告知。谢谢:)
3
0
分享到:
评论
4 楼 mail_j 2013-04-07  
不是很好用,很多情况都不能处理
1、没有区分关键字的大小写
2、关键字前后必须是空格,对于\t \n 等情况都不能匹配
3 楼 幻影之蚀 2011-08-25  
其实有一个包叫JSQLParser……
2 楼 seya 2010-04-21  
lxr0530 写道
请问
String url2 ="select ISNB,title,sale_id from book,sales" +     "where book.book_id=sales.book_id";
String url2 ="select ISNB,title,sale_id from book,sales" +     "on book.book_id=sales.book_id";
这样写有什么问题?总是出现如下错误
找了很多资料都没搞明白,谢谢回答

晕,“where”前面要空格的。。。不然跟sales连在一起了。
1 楼 lxr0530 2010-04-21  
请问
String url2 ="select ISNB,title,sale_id from book,sales" +     "where book.book_id=sales.book_id";
String url2 ="select ISNB,title,sale_id from book,sales" +     "on book.book_id=sales.book_id";
这样写有什么问题?总是出现如下错误
java.sql.SQLException: [Microsoft][SQLServer 2000 Driver for JDBC][SQLServer]第 1 行: '.' 附近有语法错误。
at com.microsoft.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.microsoft.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processErrorToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReplyToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSExecuteRequest.processReplyToken(Unknown Source)
at com.microsoft.jdbc.sqlserver.tds.TDSRequest.processReply(Unknown Source)
at com.microsoft.jdbc.sqlserver.SQLServerImplStatement.getNextResultType(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.commonTransitionToState(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.postImplExecute(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.commonExecute(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.executeQueryInternal(Unknown Source)
at com.microsoft.jdbc.base.BaseStatement.executeQuery(Unknown Source)
at TestSQLServer.main(TestSQLServer.java:15)

找了很多资料都没搞明白,谢谢回答

相关推荐

    Mysql的学习笔记01

    模块6:缓存以及缓冲引擎,对于select语句,解析sql语句之前。mysqld先检查查询缓存区域,如果能够在其中找到对应的查询,服务器不会继续解析,而是直接返回查询缓存的内容直接返回。 模块7:可插拔的存储引擎,常用...

    java开源包1

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    JAVA上百实例源码以及开源项目

     [MonthMaker.java] 月份表算法类  [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接与浏览,Java源码下载 1个目标文件 摘要:Java源码,网络相关,HTTP ...

    JAVA上百实例源码以及开源项目源代码

     [MonthMaker.java] 月份表算法类  [Pallet.java] 调色板,统一配色类 Java扫雷源码 Java生成自定义控件源代码 2个目标文件 Java实现HTTP连接与浏览,Java源码下载 1个目标文件 摘要:Java源码,网络相关,HTTP ...

    java开源包8

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    HiveSqlBloodFigure:hive血缘关系解析工具

    在数据仓库建设中,经常会使用到数据血缘追中方面的功能,本项目实现了对hql集合进行静态分析,获取hql对应的血缘图(表血缘 + 字段血缘) 项目升级内容 删除hive-exec与hadoop-common的maven依赖,使得项目更加的轻...

    java开源包11

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包6

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包4

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包9

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包101

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包5

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java开源包10

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    JAVA的数据权限设计.docx

    SQL语句高效解析处理 数据权限模块的核心之一就有SQL语句的高效解析处理,SQL处 理指根据当前登录人信息及数据权限策略生成一个带有数据权限处 理结果的SQL语句,所以这里对SQL语句的解析处理必须要求精确、 准确。...

    java开源包3

    JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...

    java jdk实列宝典 光盘源代码

    使用PreparedStatement,动态执行sql语句,UsingPreparedStatement.java; 读写二进制数据,BinaryData.java; 读写Blob数据,blob数据常以二进制形式存储比较大的文件数据,如图片、视频文件等,本文介绍如何往...

    java面试800题

    Q0027 哪些SQL语句在执行时是自动提交的? 数据定义语言DDL是自动提交的。 Q0028 索引对数据库的影响? 提高查询速度 Q0029 主外键有什么关系? 外键是从某个表的一个字段指向另外一个表的主健字段,两个字段的类型...

    JAVA WEB框架,java网站一个模块只用写一个文件

    |___GetSql.java 自动生成sql语句。在本框架中基本上不直接使用。 |___Hyberbin.java 进一步封装了数据库的操作,用户不直接对数据库操作,只要给出实体POJO类,数据可以自动查询、修改、删除、插入 servlet 用户...

    SQLServer2008查询性能优化 2/2

    通过阅读《SQL Server 2008查询性能优化》,不仅可以学习到数据库性能管理的许多知识和技巧,还有助于养成良好的编程习惯,为实现高性能的数据库应用系统打下基础。 目录 第1章 SQL查询性能调整 1 1.1 性能调整...

Global site tag (gtag.js) - Google Analytics