1. 程式人生 > >基於Java的SQL解析工具的比較與學習

基於Java的SQL解析工具的比較與學習

1、JSqlParser

gtihub 地址:  https://github.com/JSQLParser/JSqlParser.git

使用方法:

/**   
 * 
 * @Package: com.yonyou.splice 
 * @author: caozq   
 * @date: 2018年6月26日 下午12:03:52 
 */
package com.yonyou.splice;

import java.io.StringReader;
import java.util.List;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.update.Update;

/**
 * @ClassName: JsqlparserDemo
 * @Description: TODO
 * @author: caozq
 * @date: 2018年6月26日 下午12:03:52
 */
public class JsqlparserDemo {

	public static void main(String[] args) throws JSQLParserException {
		insertIntoDemo();
	}

	public static void insertIntoDemo() throws JSQLParserException {
		CCJSqlParserManager pm = new CCJSqlParserManager();

		StringBuffer stringBuffer = new StringBuffer();
		//insert into metrics_03 select pk,money,0 from metrics_01
		//stringBuffer.append("insert into tbl_name1(col1,col2) select col3,col4 from tbl_name2");
		
		
//		stringBuffer.append(" INSERT INTO FAE_CFG_CRI_AMOUNTTYPE (")
//		.append(" PK_AMOUNTTYPECRITERION,")
//		.append(" PK_AMOUNTTYPE,")
//		.append(" AMOUNTTYPE_CODE")
//		.append(" )")
//		.append(" SELECT")
//		.append(" B.PK_AMOUNTTYPECRITERION,")
//		.append(" C.PK_AMOUNTTYPE,C.CODE AMOUNTTYPE_CODE")
//		.append(" FROM FAE_AMOUNTTYPECRITERION_B  A INNER JOIN  FAE_AMOUNTTYPECRITERION B")
//		.append(" ON A.PK_AMOUNTTYPECRITERION=B.PK_AMOUNTTYPECRITERION")
//		.append(" LEFT JOIN FAE_AMOUNTTYPE C")
//		.append(" ON A.PK_AMOUNTTYPE=C.PK_AMOUNTTYPE")
//		.append(" WHERE NVL(A.DR,0)=0")
//		.append(" AND NVL(B.ISENABLE,'Y')='Y' AND NVL(B.DR,0)=0")
//		.append(" AND NVL(C.ISENABLE,'Y')='Y' AND NVL(C.DR,0)=0");
		
		
		
		stringBuffer.append(" INSERT INTO FAE_VOUCHER_B")
		.append(" PK_AMOUNTTYPECRITERION,")
		.append(" PK_AMOUNTTYPE,")
		.append(" AMOUNTTYPE_CODE")
		.append(" )")
		.append(" WITH FAE_ASSFLOW_B_TEMP AS (")
		.append("  SELECT")
		.append(" T1.RECORD_DT ,")
		.append(" T1.DEAL_DT ,")
		.append(" T1.DEAL_CODE ")
		.append(" CASE WHEN T1.CDFLAG='C' THEN  DEAL_AMT")
		.append(" WHEN T1.CDFLAG='D' THEN -DEAL_AMT")
		.append(" ELSE 0 END DEAL_AMT")
		.append(" ON A.PK_AMOUNTTYPE=C.PK_AMOUNTTYPE")
		.append(" WHERE NVL(A.DR,0)=0")
		.append(" AND NVL(B.ISENABLE,'Y')='Y' AND NVL(B.DR,0)=0")
		.append(" AND NVL(C.ISENABLE,'Y')='Y' AND NVL(C.DR,0)=0");
		
		
		Statement statement = pm.parse(new StringReader(stringBuffer.toString()));
		
		
		

		if (statement instanceof Insert) {
			// 獲得Update物件
			Insert istatement = (Insert) statement;
			// 獲得表名
			System.out.println("table:" + istatement.getTable());

			List<Column> columns = istatement.getColumns();

			if(null == columns){
				columns = null;//獲取所有的表字段
			}
			
			//
			
			Select sele = istatement.getSelect();
			PlainSelect body = (PlainSelect) sele.getSelectBody();
			String selectStr = body.toString();
			
			System.out.println("-------");
			// 獲得where條件表示式
			Expression where = null;
			if(istatement.getSetExpressionList().size()!=0){
				where = istatement.getSetExpressionList().get(0);
				// 初始化接收穫得到的欄位資訊
				StringBuffer allColumnNames = new StringBuffer();
				// BinaryExpression包括了整個where條件,
				// 例如:AndExpression/LikeExpression/OldOracleJoinBinaryExpression
				if (where instanceof BinaryExpression) {
					allColumnNames = getColumnName((BinaryExpression) (where), allColumnNames);
					System.out.println("allColumnNames:" + allColumnNames.toString());
				}
			}
			
		}
	}

	public static void updateDemo() throws JSQLParserException {
		CCJSqlParserManager pm = new CCJSqlParserManager();

		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("update ac_operator op ");
		stringBuffer.append("set op.errcount=(");
		stringBuffer.append("(select case when op1.errcount is null then 0 else op1.errcount end as errcount ");
		stringBuffer.append("from ac_operator op1 ");
		stringBuffer.append("where op1.loginname = '中國' )+1");
		stringBuffer.append("),lastlogin='中國' ");
		stringBuffer.append("where PROCESS_ID=");
		stringBuffer.append("(select distinct g.id from tempTable g where g.ID='中國')");
		stringBuffer.append("and columnName2 = '890' and columnName3 = '678' and columnName4 = '456'");

		Statement statement = pm.parse(new StringReader(stringBuffer.toString()));

		if (statement instanceof Update) {
			// 獲得Update物件
			Update updateStatement = (Update) statement;
			// 獲得表名
			System.out.println("table:" + updateStatement.getTables().get(0).getName());

			List<Column> columns = updateStatement.getColumns();

			for (Column column : columns) {
				System.out.println(column.getColumnName());
			}
			// 獲得where條件表示式
			Expression where = updateStatement.getWhere();
			// 初始化接收穫得到的欄位資訊
			StringBuffer allColumnNames = new StringBuffer();
			// BinaryExpression包括了整個where條件,
			// 例如:AndExpression/LikeExpression/OldOracleJoinBinaryExpression
			if (where instanceof BinaryExpression) {
				allColumnNames = getColumnName((BinaryExpression) (where), allColumnNames);
				System.out.println("allColumnNames:" + allColumnNames.toString());
			}
		}
	}

	/**
	 * 獲得where條件欄位中列名,以及對應的操作符 @Title: getColumnName @Description:
	 * TODO(這裡用一句話描述這個方法的作用) @param @param expression @param @param
	 * allColumnNames @param @return 設定檔案 @return StringBuffer 返回型別 @throws
	 */
	private static StringBuffer getColumnName(Expression expression, StringBuffer allColumnNames) {

		String columnName = null;
		if (expression instanceof BinaryExpression) {
			// 獲得左邊表示式
			Expression leftExpression = ((BinaryExpression) expression).getLeftExpression();
			// 如果左邊表示式為Column物件,則直接獲得列名
			if (leftExpression instanceof Column) {
				// 獲得列名
				columnName = ((Column) leftExpression).getColumnName();
				allColumnNames.append(columnName);
				allColumnNames.append(":");
				// 拼接操作符
				allColumnNames.append(((BinaryExpression) expression).getStringExpression());
				// allColumnNames.append("-");
			}
			// 否則,進行迭代
			else if (leftExpression instanceof BinaryExpression) {
				getColumnName((BinaryExpression) leftExpression, allColumnNames);
			}

			// 獲得右邊表示式,並分解
			Expression rightExpression = ((BinaryExpression) expression).getRightExpression();
			if (rightExpression instanceof BinaryExpression) {
				Expression leftExpression2 = ((BinaryExpression) rightExpression).getLeftExpression();
				if (leftExpression2 instanceof Column) {
					// 獲得列名
					columnName = ((Column) leftExpression2).getColumnName();
					allColumnNames.append("-");
					allColumnNames.append(columnName);
					allColumnNames.append(":");
					// 獲得操作符
					allColumnNames.append(((BinaryExpression) rightExpression).getStringExpression());
				}
			}
		}
		return allColumnNames;
	}
}

2  Apache Calcite(以Mysql為例)

public static String convertMySql(String sql)
            throws SQLException, SqlParseException, ValidationException, RelConversionException {
        Connection aConnection = JDBCConnection.getInstance().getConnection();
        try{
            SqlDialectFactory aSqlDialectFactory = new SqlDialectFactoryImpl();
            SqlDialect aSqlDialect = aSqlDialectFactory.create(aConnection.getMetaData());
            SqlDialect.DatabaseProduct aDatabaseProduct =
                    SqlDialect.getProduct(aConnection.getMetaData().getDatabaseProductName(), "UNUSED");
            String schemaName = getSchemaName(aDatabaseProduct, aConnection);
            String catalog = aConnection.getCatalog();
            FrameworkConfig aConfig = createConfig(dataSource, aDatabaseProduct, catalog, schemaName);
            Planner aPlanner = Frameworks.getPlanner(aConfig);
            SqlNode aQuery = aPlanner.parse(sql);
            aQuery = aPlanner.validate(aQuery);
            RelNode aRelNode = aPlanner.rel(aQuery).project();
            RelToSqlConverter aSqlConverter = new RelToSqlConverter(aSqlDialect);
            SqlNode aSqlNode = aSqlConverter.visitChild(0, aRelNode).asStatement();
            System.out.println(sql);
            System.out.println(aQuery);
            System.out.println(RelOptUtil.toString(aRelNode));
            System.out.println(aSqlNode);
            return sql;
        }catch(Exception e){
            e.getStackTrace();
        }
        
        return null;
    }
    

3  JavaCC  

JavaCC 是一個詞法分析生成器和語法分析生成器。 詞法分析和語法分析是處理輸入字元序列的軟體構件, 編譯器和直譯器協同詞法分析和語法分析來“解密” 程式檔案。

  • 使用遞迴下降語法解析,LL(k)。其中,第一個L表示從左到右掃描輸入;第二個L表示每次都進行最左推導(在推導語法樹的過程中每次都替換句型中最左的非終結符為終結符。類似還有最右推導);k表示的是每次向前探索(lookahead)k個終結符

  • 詞法規則,語法規則定義在同一檔案中,就是.jj檔案。

  • jjTree可以幫助更好的語法分析(因為好像沒用過,不好說啊)

  • 可定製生成的行為,如對字母的大小寫是否敏感。不如設計資料庫sql語句的時候應該使用關鍵字大小寫不敏感。

  • 更向前一步解決移進規約。當文法本身存在二義性的時候有時候通過設定lookahead為k能解決問題,帶來的問題就是增加編譯時間,所以最好的方法是修改二義性文法為無二義性文法。

參考:https://blog.csdn.net/newpidian/article/details/52964017