`
seya
  • 浏览: 360525 次
  • 性别: 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)

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

相关推荐

    java检查sql语法是否正确

    在Java中,ANTLR常被用来解析SQL语句,生成抽象语法树(AST),从而可以进行语法检查。`antlr-2.7.6rc1.jar`是ANTLR的一个版本,包含了必要的类库,可以生成解析SQL语法所需的解析器。 `commons-lang.jar`是Apache ...

    Java 使用注解拼接SQL语句

    "Java使用注解拼接SQL语句"是一个常见的实践,它使得动态构建SQL查询变得更为简洁和可维护。这种技术通常与ORM(对象关系映射)框架如MyBatis或Hibernate结合使用,但也可以通过自定义处理逻辑实现。 1. **自定义...

    动态封装SQL语句,配置文件的调用与封装

    在IT行业中,数据库操作是应用开发中的重要环节,而动态封装SQL语句和配置文件的调用与封装是提升程序灵活性、可维护性的重要技术手段。下面将详细讲解这两个概念及其应用。 首先,动态封装SQL语句指的是在运行时...

    java批量执行SQL

    在Java中,主要通过`PreparedStatement`的`addBatch()`方法将多条SQL语句添加到批处理中,再调用`executeBatch()`方法一次性提交所有SQL语句。 #### 三、Java批量执行SQL的具体实现 以下是对给定部分代码的详细...

    Java Sql 格式化工具

    它可能包含了读取SQL语句、解析语法结构、应用格式规则以及生成格式化后SQL的逻辑。对于想要自定义或扩展格式化功能的开发者来说,这是一个重要的起点。 使用这些文件,开发者可以按照以下步骤进行操作: 1. 首先...

    java读取数据库表中内容转存sql文件,然后解析执行此文件

    这项操作可能涉及到数据库连接、数据查询、文件操作以及SQL语句的构建与执行。以下将详细介绍这个过程的各个环节。 1. **数据库连接**:在Java中,我们可以使用JDBC(Java Database Connectivity)来连接数据库。...

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

    2. **基本用法**:展示如何创建一个解析器实例,并使用它来解析SQL语句,转化为AST对象。 3. **AST遍历**:解释如何遍历AST,访问每个节点,获取SQL语句的组成部分,如表名、列名、条件等。 4. **修改与重构SQL**...

    Java开发的sql生成器

    在集成这个Java SQL生成器到你的项目中,你需要按照文档的指示配置数据库连接,然后在需要的地方调用相应的API来生成SQL语句。通过这种方式,你可以专注于业务逻辑,而不是繁琐的SQL拼写,提高开发效率并降低出错...

    java使用druid进行sql解析,可解析出物理表,外层投影列对应的物理表和字段

    然后,我们调用`parseStatementList()`方法解析SQL并获取一个包含所有解析结果的列表。每个解析结果都是一个`SQLStatement`对象,你可以遍历这些对象,获取到SQL中的物理表名、投影列以及它们对应的字段。 为了获取...

    java如何获得数据库表中各字段的字段名

    使用`PreparedStatement`执行SQL查询语句(这里假设查询的是名为`dept`的表),并获取结果集`ResultSet`。 4. **获取元数据**: ```java ResultSetMetaData metaData = resultSet.getMetaData(); ``` 通过调用...

    Hibernate中Sql语句

    虽然HQL是Hibernate提供的面向对象的语言,能够方便地映射到Java对象,但在某些场景下使用原生SQL更加灵活高效,例如: - **复杂查询**:当需要执行复杂的数据库操作时,如分组、聚合、子查询等,原生SQL能更好地...

    java十分经典的jdbc增删改查程序,含sql语句

    - 性能优化:SQL语句预编译后,数据库只需解析一次。 - 安全性:参数化查询能有效防止SQL注入攻击。 - 可读性:对于复杂的SQL,预编译后的语句更易读。 9. **连接池**: 在实际应用中,为了提高性能和资源利用...

    注解反射生成SQL语句

    这样,当我们需要生成SQL语句时,可以扫描带有这些注解的类和字段,根据注解信息构建出相应的SQL语句。 反射则是Java提供的一种在运行时检查和修改程序的能力。它允许我们获取类的信息(如类名、方法、字段),甚至...

    java8源码-sqlParser:使用java antlr4的sql解析器

    词法分析器将输入的SQL语句分解为一个个符号(Token),而语法解析器则根据这些符号构建出抽象语法树(AST),便于后续处理和执行。 在这个项目中,开发者可能首先定义了一个SQL的语法规则文件,例如`Sql.g4`,其中...

    基于gudusoft的sql解析项目

    在项目中引入这个库,然后调用提供的API,传递SQL语句进行解析。 总的来说,基于Gudusoft的SQL解析项目为开发者提供了强大的SQL处理能力,帮助他们更好地理解和操作数据库,提升开发效率。通过深入理解和利用这个...

    基于Java的实例源码-SQL解析类库 SQLJEP.zip

    然后,你可以使用`parse()`方法解析SQL语句,并得到一个`Expression`对象。这个对象可以被进一步分析、修改或执行。 2. **SQL解析过程**:在解析过程中,SQLJEP会将SQL语句分解成一个个元素,如选择列、表名、条件...

    基于java的SQL解析类库 SQLJEP.zip

    SQLJEP是一个基于Java的SQL解析库,专为处理和解析SQL语句而设计。它提供了将SQL表达式转化为抽象语法树(AST)的能力,进而可以进行进一步的分析、操作或转换。在Java应用程序中,当需要对用户输入的SQL进行验证、...

    java.sql.与java.util

    要从`java.util.Date`转换为`java.sql.Date`,可以调用`java.util.Date`的`getTime()`方法获取时间戳,然后用这个时间戳初始化`java.sql.Date`。相反,从`java.sql.Date`转换回`java.util.Date`,只需传入`java.sql....

    java连sqlserver数据库详细解析哦

    本文将深入探讨如何使用Java API(如JDBC)来连接到SQL Server数据库,并提供详细的步骤和示例代码。 首先,我们需要了解Java Database Connectivity (JDBC),这是Java标准库中的一个API,用于与各种类型的数据库...

    基于Java的SQL解析类库 SQLJEP.zip

    而Java开发者经常需要在应用程序中处理SQL语句,这通常涉及到SQL的解析和执行。"基于Java的SQL解析类库 SQLJEP.zip" 提供了一个解决方案,帮助Java开发者更有效地处理SQL相关任务。 SQLJEP(SQL Java Expression ...

Global site tag (gtag.js) - Google Analytics