基本思想:
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请告知。谢谢:)
分享到:
相关推荐
在Java中,ANTLR常被用来解析SQL语句,生成抽象语法树(AST),从而可以进行语法检查。`antlr-2.7.6rc1.jar`是ANTLR的一个版本,包含了必要的类库,可以生成解析SQL语法所需的解析器。 `commons-lang.jar`是Apache ...
"Java使用注解拼接SQL语句"是一个常见的实践,它使得动态构建SQL查询变得更为简洁和可维护。这种技术通常与ORM(对象关系映射)框架如MyBatis或Hibernate结合使用,但也可以通过自定义处理逻辑实现。 1. **自定义...
在IT行业中,数据库操作是应用开发中的重要环节,而动态封装SQL语句和配置文件的调用与封装是提升程序灵活性、可维护性的重要技术手段。下面将详细讲解这两个概念及其应用。 首先,动态封装SQL语句指的是在运行时...
在Java中,主要通过`PreparedStatement`的`addBatch()`方法将多条SQL语句添加到批处理中,再调用`executeBatch()`方法一次性提交所有SQL语句。 #### 三、Java批量执行SQL的具体实现 以下是对给定部分代码的详细...
它可能包含了读取SQL语句、解析语法结构、应用格式规则以及生成格式化后SQL的逻辑。对于想要自定义或扩展格式化功能的开发者来说,这是一个重要的起点。 使用这些文件,开发者可以按照以下步骤进行操作: 1. 首先...
这项操作可能涉及到数据库连接、数据查询、文件操作以及SQL语句的构建与执行。以下将详细介绍这个过程的各个环节。 1. **数据库连接**:在Java中,我们可以使用JDBC(Java Database Connectivity)来连接数据库。...
2. **基本用法**:展示如何创建一个解析器实例,并使用它来解析SQL语句,转化为AST对象。 3. **AST遍历**:解释如何遍历AST,访问每个节点,获取SQL语句的组成部分,如表名、列名、条件等。 4. **修改与重构SQL**...
在集成这个Java SQL生成器到你的项目中,你需要按照文档的指示配置数据库连接,然后在需要的地方调用相应的API来生成SQL语句。通过这种方式,你可以专注于业务逻辑,而不是繁琐的SQL拼写,提高开发效率并降低出错...
然后,我们调用`parseStatementList()`方法解析SQL并获取一个包含所有解析结果的列表。每个解析结果都是一个`SQLStatement`对象,你可以遍历这些对象,获取到SQL中的物理表名、投影列以及它们对应的字段。 为了获取...
使用`PreparedStatement`执行SQL查询语句(这里假设查询的是名为`dept`的表),并获取结果集`ResultSet`。 4. **获取元数据**: ```java ResultSetMetaData metaData = resultSet.getMetaData(); ``` 通过调用...
虽然HQL是Hibernate提供的面向对象的语言,能够方便地映射到Java对象,但在某些场景下使用原生SQL更加灵活高效,例如: - **复杂查询**:当需要执行复杂的数据库操作时,如分组、聚合、子查询等,原生SQL能更好地...
- 性能优化:SQL语句预编译后,数据库只需解析一次。 - 安全性:参数化查询能有效防止SQL注入攻击。 - 可读性:对于复杂的SQL,预编译后的语句更易读。 9. **连接池**: 在实际应用中,为了提高性能和资源利用...
这样,当我们需要生成SQL语句时,可以扫描带有这些注解的类和字段,根据注解信息构建出相应的SQL语句。 反射则是Java提供的一种在运行时检查和修改程序的能力。它允许我们获取类的信息(如类名、方法、字段),甚至...
词法分析器将输入的SQL语句分解为一个个符号(Token),而语法解析器则根据这些符号构建出抽象语法树(AST),便于后续处理和执行。 在这个项目中,开发者可能首先定义了一个SQL的语法规则文件,例如`Sql.g4`,其中...
在项目中引入这个库,然后调用提供的API,传递SQL语句进行解析。 总的来说,基于Gudusoft的SQL解析项目为开发者提供了强大的SQL处理能力,帮助他们更好地理解和操作数据库,提升开发效率。通过深入理解和利用这个...
然后,你可以使用`parse()`方法解析SQL语句,并得到一个`Expression`对象。这个对象可以被进一步分析、修改或执行。 2. **SQL解析过程**:在解析过程中,SQLJEP会将SQL语句分解成一个个元素,如选择列、表名、条件...
SQLJEP是一个基于Java的SQL解析库,专为处理和解析SQL语句而设计。它提供了将SQL表达式转化为抽象语法树(AST)的能力,进而可以进行进一步的分析、操作或转换。在Java应用程序中,当需要对用户输入的SQL进行验证、...
要从`java.util.Date`转换为`java.sql.Date`,可以调用`java.util.Date`的`getTime()`方法获取时间戳,然后用这个时间戳初始化`java.sql.Date`。相反,从`java.sql.Date`转换回`java.util.Date`,只需传入`java.sql....
本文将深入探讨如何使用Java API(如JDBC)来连接到SQL Server数据库,并提供详细的步骤和示例代码。 首先,我们需要了解Java Database Connectivity (JDBC),这是Java标准库中的一个API,用于与各种类型的数据库...
而Java开发者经常需要在应用程序中处理SQL语句,这通常涉及到SQL的解析和执行。"基于Java的SQL解析类库 SQLJEP.zip" 提供了一个解决方案,帮助Java开发者更有效地处理SQL相关任务。 SQLJEP(SQL Java Expression ...