diff --git a/pom.xml b/pom.xml index 19f2e97..bc96c39 100644 --- a/pom.xml +++ b/pom.xml @@ -24,24 +24,13 @@ com.github.jsqlparser jsqlparser - 0.9.1 + 3.2 - - - org.springframework.boot - spring-boot-starter - - - org.springframework.boot - spring-boot-starter-test + org.junit.jupiter + junit-jupiter + 5.6.3 test - - - org.junit.vintage - junit-vintage-engine - - diff --git a/src/main/java/com/luop/MySelectVisitor.java b/src/main/java/com/luop/MySelectVisitor.java index 259e2a2..0363063 100644 --- a/src/main/java/com/luop/MySelectVisitor.java +++ b/src/main/java/com/luop/MySelectVisitor.java @@ -1,90 +1,217 @@ package com.luop; +/** + * @author wxn + * @date 2023/8/7 + */ import net.sf.jsqlparser.expression.*; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.schema.Table; import net.sf.jsqlparser.statement.select.*; -import org.springframework.util.StringUtils; import java.util.*; + /** - * @Author: luoping - * @Date: 2019/11/5 09:50 - * @Description: + * @Author: wxn + * @Date: 2023/05/25 + * 用递归来解决多层嵌套子查询的问题 */ -public class MySelectVisitor implements SelectVisitor { - //用来存储查询SQL语句涉及到的表和字段 - @Override - public void visit(PlainSelect pSelect) { - processFromItem(pSelect, pSelect.getFromItem()); - //子查询处理,joins处理 +public class MySelectVisitor { + /** + * 递归visit,通过对子查询visit并合并columnsMapping实现整体语句的解析 + * @param setList + * @param columnsMapping + */ + public void visit(SetOperationList setList, Map> columnsMapping) { + List setSelectBodies = setList.getSelects(); + List>> subSubQueryColumnsMappings = new ArrayList<>(setSelectBodies.size()); + for (SelectBody subSubselectBody : setSelectBodies){ + Map> map = new HashMap<>(); + this.visit((PlainSelect) subSubselectBody, map, false); + subSubQueryColumnsMappings.add(map); + } + mergeSetQueryColumnsMappings(subSubQueryColumnsMappings, columnsMapping); + } + + public void visit(PlainSelect pSelect, Map> columnsMapping, boolean isSubQuery) { + processFromItem(pSelect, pSelect.getFromItem(), columnsMapping, isSubQuery); List joins = pSelect.getJoins(); if (joins != null) { for (Join join : joins) { - tableAliasName = ""; //重置表别名 - processFromItem(pSelect, join.getRightItem()); + //将join的语句当作from语句处理(都可能是table或者subQuery) + FromItem rightItem = join.getRightItem(); + visitFromItem(columnsMapping, rightItem); + } + } + } + + /** + * 递归visit,通过对子查询visit并合并columnsMapping实现整体语句的解析 + * @param pSelect + * @param columnsMapping + */ + private void processFromItem(PlainSelect pSelect, FromItem fromItem, Map> columnsMapping, boolean isSubQuery) { + //解析select字段 + List selectColumn = pSelect.getSelectItems(); + for (SelectItem selectItem : selectColumn) { + SelectExpressionItem expressionItem = (SelectExpressionItem) selectItem; + String aliasName; + Expression expression = expressionItem.getExpression(); + if (isSubQuery){ + if (expressionItem.getAlias() == null){ + aliasName = expression.toString(); + }else { + if (expression.toString().contains(".")){ + aliasName = expression.toString().split("[.]")[0] + "." + expressionItem.getAlias().getName(); + }else { + aliasName = expressionItem.getAlias().getName(); + } + } + + }else { + if (expressionItem.getAlias() == null) { + //类似select count(0) from table的情况 + if (expression instanceof Function){ + aliasName = expression.toString(); + }else { + aliasName = ((Column) expressionItem.getExpression()).getColumnName(); + } + } else { //有别名 + aliasName = expressionItem.getAlias().getName(); + } } + analysisFunction(expression, columnsMapping, aliasName); + } + if (columnsMapping.isEmpty()){ + return; } + + //解析from语句 + visitFromItem(columnsMapping, fromItem); } /** - * 处理fromItem,并将处理结果保存起来 + * 用在join的右子句或者from子句,都可能出现表或者子查询 * - * @param pSelect 当前from信息对应的select信息 - * @param fromItem from信息 + * @param columnsMapping + * @param fromItem */ - private Map columnMappingMap = new HashMap<>(); //别名:真实字段名 - private Map> map = new HashMap<>(); //装查询字段 - private boolean state = true; - private String tableAliasName; + private void visitFromItem(Map> columnsMapping, FromItem fromItem) { + String tableAliasName = null; + if (fromItem instanceof Table) { + String tableName = getTableName((Table) fromItem); + if (fromItem.getAlias() != null) { + tableAliasName = fromItem.getAlias().getName(); + } + tableAliasName = Objects.isNull(tableAliasName) ? tableName : tableAliasName; + setColumnMappingMap(columnsMapping, tableName, tableAliasName); + } else if (fromItem instanceof SubSelect) { + Map> subQueryColumnsMapping = new HashMap<>(); + // 如果是UNION则需要进一步处理 + SelectBody subSelectBody = ((SubSelect) fromItem).getSelectBody(); + if (subSelectBody instanceof SetOperationList){ + tableAliasName = Objects.requireNonNull(fromItem.getAlias()).getName(); + List setSelectBodies = ((SetOperationList) subSelectBody).getSelects(); + List>> subSubQueryColumnsMappings = new ArrayList<>(setSelectBodies.size()); + for (SelectBody subSubselectBody : setSelectBodies){ + Map> map = new HashMap<>(); + this.visit((PlainSelect) subSubselectBody, map, true); + updateSubQueryMappingWithSubQueryAliasName(map, tableAliasName); + subSubQueryColumnsMappings.add(map); + } + mergeSetQueryColumnsMappings(subSubQueryColumnsMappings, subQueryColumnsMapping); + }else { + tableAliasName = Objects.requireNonNull(fromItem.getAlias()).getName(); + this.visit((PlainSelect) subSelectBody, subQueryColumnsMapping, true); + //先处理子查询的结果 + updateSubQueryMappingWithSubQueryAliasName(subQueryColumnsMapping, tableAliasName); + } + //解析结束,开始合并并替换别名,生成最后的columnsMapping + mergeSubQueryColumnMapIntoColumnMapping(columnsMapping, subQueryColumnsMapping); + } + } + + private void mergeSetQueryColumnsMappings(List>> maps, Map> resultMap){ + for (Map>map : maps){ + for (String key: map.keySet()){ + if (resultMap.containsKey(key)){ + Set columns = resultMap.get(key); + columns.addAll(map.get(key)); + resultMap.put(key, columns); + }else { + resultMap.put(key, map.get(key)); + } + } + } + } //处理查询字段里的函数 private Expression analysisFunction(Expression expression, Map> map, String aliasName) { while (judgeExpression(expression)) { - while (expression instanceof CaseExpression) { //case when 函数 - Expression elseExpression = ((CaseExpression) expression).getElseExpression(); //获取else表达式 - if (judgeExpression(elseExpression)) + + while (expression instanceof CaseExpression) { + //case when 函数 + Expression elseExpression = ((CaseExpression) expression).getElseExpression(); + //获取else表达式 + if (judgeExpression(elseExpression)) { expression = elseExpression; - else if (!Objects.isNull(elseExpression) && judgeValueExpression(elseExpression)) + } else if (!Objects.isNull(elseExpression) && judgeValueExpression(elseExpression)) { putSelectMap(elseExpression, map, aliasName); - - for (Expression whenClause : ((CaseExpression) expression).getWhenClauses()) { //when条件 + } + for (Expression whenClause : ((CaseExpression) expression).getWhenClauses()) { + //when条件 WhenClause clause = (WhenClause) whenClause; - expression = analysisFunction(clause.getThenExpression(), map, aliasName); //获取then表达式 + //获取then表达式 + expression = analysisFunction(clause.getThenExpression(), map, aliasName); } } - while (expression instanceof Function) { //函数 + while (expression instanceof Function) { + //函数 Function function = (Function) expression; + //没有参数的函数直接返回,没有分析价值 + if (function.getParameters() == null){ + return expression; + } List expressions = function.getParameters().getExpressions(); for (Expression ex : expressions) { if (judgeExpression(ex)) { expression = analysisFunction(ex, map, aliasName); } else { - if (judgeValueExpression(ex)) + if (judgeValueExpression(ex)) { expression = analysisFunction(ex, map, aliasName); + }else { + //函数的参数是常量的直接返回,没有分析价值 + return expression; + } } } } - while (expression instanceof CastExpression) { //cast函数 + while (expression instanceof CastExpression) { + //cast函数 Expression leftExpression = ((CastExpression) expression).getLeftExpression(); - if (judgeExpression(leftExpression)) + if (judgeExpression(leftExpression)) { expression = leftExpression; + } } - while (expression instanceof BinaryExpression) { //concat函数 - Expression leftExpression = ((BinaryExpression) expression).getLeftExpression(); //左侧表达式; + while (expression instanceof BinaryExpression) { + //concat函数 + //左侧表达式; + Expression leftExpression = ((BinaryExpression) expression).getLeftExpression(); if (judgeExpression(leftExpression)) { expression = leftExpression; } else { if (judgeValueExpression(leftExpression)) { putSelectMap(leftExpression, map, aliasName); } - expression = ((BinaryExpression) expression).getRightExpression(); // 右侧表达式 + // 右侧表达式 + expression = ((BinaryExpression) expression).getRightExpression(); } } } - if (judgeValueExpression(expression)) + if (judgeValueExpression(expression)) { putSelectMap(expression, map, aliasName); + } return expression; } @@ -92,119 +219,101 @@ else if (!Objects.isNull(elseExpression) && judgeValueExpression(elseExpression) private void putSelectMap(Expression expression, Map> map, String aliasName) { Set set = new HashSet<>(); if (!map.containsKey(aliasName)) { - set.add(expression.toString()); + set.add(clearString(expression.toString())); } else { set = map.get(aliasName); - set.add(expression.toString()); + set.add(clearString(expression.toString())); } - map.put(aliasName, set); + map.put(clearString(aliasName), set); } - private void processFromItem(PlainSelect pSelect, FromItem fromItem) { - if (state) { //将查询字段按 字段名:别名 封装到map中 - List selectColumn = pSelect.getSelectItems(); - for (SelectItem selectItem : selectColumn) { - SelectExpressionItem expressionItem = (SelectExpressionItem) selectItem; - String aliasName;//字段别名 - if (expressionItem.getAlias() == null) { //没有别名 - aliasName = ((Column) expressionItem.getExpression()).getColumnName(); - } else { //有别名 - aliasName = expressionItem.getAlias().getName(); - } - Expression expression = expressionItem.getExpression(); - analysisFunction(expression, map, aliasName); - } - } - if (!state && fromItem.getAlias() != null) { - if (!StringUtils.isEmpty(tableAliasName)) - columnMappingMap.put(tableAliasName, fromItem.getAlias().getName()); //外层子查询别名:子查询别名 - } - if (fromItem instanceof Table) { //表 - String tableName = getTableName((Table) fromItem); - if (fromItem.getAlias() != null) { - tableAliasName = fromItem.getAlias().getName(); - } - columnMappingMap.put(tableAliasName, tableName); //表别名:表真实名 - tableAliasName = Objects.isNull(tableAliasName) ? tableName : tableAliasName; - } else if (fromItem instanceof SubSelect) { //子查询 - state = false; - tableAliasName = Objects.requireNonNull(fromItem.getAlias()).getName(); //表别名 - ((SubSelect) fromItem).getSelectBody().accept(this); - } - } - - //获取表名(包含 数据库名,表空间名) - private String getTableName(Table fromItem) { - String name = fromItem.getName(); //表名 - String databaseName = fromItem.getDatabase().getDatabaseName(); //数据库名 - String schemaName = fromItem.getSchemaName(); //表空间名 - if (Objects.isNull(databaseName) && !Objects.isNull(schemaName)) - return schemaName + "." + name; - if (!Objects.isNull(databaseName)) { - return databaseName + "." + (schemaName == null ? "" : schemaName) + "." + name; - } - return name; - } - @Override - public void visit(SetOperationList sOperationList) { - //union all会用到 - List pSelects = sOperationList.getPlainSelects(); - for (PlainSelect pSelect : pSelects) { - this.visit(pSelect); + /** + * 将子查询中的字段映射关系 合并到select解析结果中 + * 如:select t1.field1 from (select field1 from table1) as t1, + * 提取的select结果为: field1 -> {t1.field1} + * 经过子查询别名更新后的子查询字段映射关系为: t1.field1 -> {table1.field1} + * 合并后的最终结果为: field1 -> {table1.field1} + * + * @param columnsMapping + * @param subQueryMapping + */ + private void mergeSubQueryColumnMapIntoColumnMapping(Map> columnsMapping, Map> subQueryMapping) { + for (String str : columnsMapping.keySet()) { + Set newSet = new HashSet<>(); + Set set = columnsMapping.get(str); + for (String s : set) { + newSet.addAll(subQueryMapping.containsKey(s) ? subQueryMapping.get(s) : Collections.singleton(s)); + } + columnsMapping.put(str, newSet); } } - @Override - public void visit(WithItem wItem) { -// System.out.println("WithItem=========" + wItem); - } - /** - * 返回SQL解析的最终结果 - * 最终结果为血缘关系(别名字段来源真实表字段) + * 用子查询的别名来更新子查询中的字段映射关系 + * 如:(select field1 from table1) as t1 + * 提取的子查询字段映射关系为: field1->{table1.field1} + * 经过子查询别名映射后,更新为: t1.field1->{table1.field1} * - * @return + * @param columnsMapping + * @param subQueryAliasName */ - Map> getSqlParseResult() { -// System.out.println("columnMappingMap: " + columnMappingMap); -// System.out.println("map:" + map); - return getColumnMapping(); + private void updateSubQueryMappingWithSubQueryAliasName(Map> columnsMapping, String subQueryAliasName) { + Map> map2 = new HashMap<>(); + for (String str : columnsMapping.keySet()) { + if (str.contains(".")) { + String[] split = str.split("[.]"); + map2.put(subQueryAliasName + "." + split[1], columnsMapping.get(str)); + } else { + map2.put(subQueryAliasName + "." + str, columnsMapping.get(str)); + } + } + columnsMapping.clear(); + columnsMapping.putAll(map2); } - //封装血缘关系 - private Map> getColumnMapping() { - Map> map2 = new HashMap<>(); - Set keySet = map.keySet(); - for (String str : keySet) { + private void setColumnMappingMap(Map> columnsMapping, String tableName, String tableAliasName) { + for (String str : columnsMapping.keySet()) { Set columnSet = new HashSet<>(); - Set set = map.get(str); + Set set = columnsMapping.get(str); for (String s : set) { if (s.contains(".")) { String[] split = s.split("[.]"); - s = getMap(split[0]) + "." + split[1]; + s = (tableAliasName.equalsIgnoreCase(split[0])) ? tableName + "." + split[1] : s; + } else { + s = tableName + "." + s; } columnSet.add(s); - map2.put(str, columnSet); } + columnsMapping.put(str, columnSet); } - return map2; } - //循环查询直到找到真实表名 - private String getMap(String str) { - while (columnMappingMap.containsKey(str)) { - str = columnMappingMap.get(str); + //获取表名(包含 数据库名,表空间名) + //TODO: 暂时不处理跨库,仅对表名进行限制 + private String getTableName(Table fromItem) { + //表名 + String name = fromItem.getName(); + //数据库名 + String databaseName = fromItem.getDatabase().getDatabaseName(); + //表空间名 + String schemaName = fromItem.getSchemaName(); + if (Objects.isNull(databaseName) && !Objects.isNull(schemaName)) { + return schemaName + "." + name; } - return str; + if (!Objects.isNull(databaseName)) { + return databaseName + "." + (schemaName == null ? "" : schemaName) + "." + name; + } + return name; } + //判断查询字段中是否包含函数 private boolean judgeExpression(Expression expression) { - if (!Objects.isNull(expression)) + if (!Objects.isNull(expression)) { return expression instanceof Function || expression instanceof CastExpression || expression instanceof CaseExpression || expression instanceof BinaryExpression; - else - return false; + } + return false; } //判断expression是否为值类型 @@ -212,12 +321,7 @@ private boolean judgeValueExpression(Expression expression) { return !(expression instanceof StringValue) && !(expression instanceof DoubleValue) && !(expression instanceof LongValue); } - /* - * Function: isnull,ifnull,wm_concat,replace - * CastExpression: cast, - * CaseExpression: case when - * StringValue: "" - * LongValue: 11 - * DoubleValue: 2.35 - * */ + private String clearString(String s){ + return s.replace("`",""); + } } diff --git a/src/main/java/com/luop/MySelectVisitor2.java b/src/main/java/com/luop/MySelectVisitor2.java deleted file mode 100644 index 5857bf5..0000000 --- a/src/main/java/com/luop/MySelectVisitor2.java +++ /dev/null @@ -1,137 +0,0 @@ -package com.luop; - -import net.sf.jsqlparser.expression.Expression; -import net.sf.jsqlparser.expression.operators.conditional.AndExpression; -import net.sf.jsqlparser.expression.operators.relational.OldOracleJoinBinaryExpression; -import net.sf.jsqlparser.schema.Table; -import net.sf.jsqlparser.statement.select.*; -import org.springframework.util.CollectionUtils; - -import java.util.*; - -/** - * @Author: luoping - * @Date: 2019/11/5 09:50 - * @Description: 表之间的关系 - */ -public class MySelectVisitor2 implements SelectVisitor { - //用来存储查询SQL语句涉及到的表和字段 - @Override - public void visit(PlainSelect pSelect) { - processFromItem(pSelect, pSelect.getFromItem()); - //子查询处理,joins处理 - List joins = pSelect.getJoins(); - if (joins != null) { - for (Join join : joins) { - processFromItem(pSelect, join.getRightItem()); - } - } - } - - private Map tableMap = new HashMap<>(); - private Map> mappingMap = new HashMap<>(); - - private void processFromItem(PlainSelect pSelect, FromItem fromItem) { - List joins = pSelect.getJoins(); - if (!CollectionUtils.isEmpty(joins) && joins.size() > 0) { - putTableMap(fromItem); - - //解析join - for (Join join : joins) { - putTableMap(join.getRightItem()); - Expression onExpression = join.getOnExpression(); - analysisCondition(onExpression); - } - } - } - - //解析表间字段映射关系 - private Expression analysisCondition(Expression expression) { - while (expression instanceof AndExpression) { - AndExpression andExpression = (AndExpression) expression; - analysisCondition(andExpression.getRightExpression()); - Expression leftExpression = andExpression.getLeftExpression(); - if (leftExpression instanceof OldOracleJoinBinaryExpression) { - expression = leftExpression; - } else { - expression = analysisCondition(leftExpression); - } - } - if (expression instanceof OldOracleJoinBinaryExpression) { - String leftStr = ((OldOracleJoinBinaryExpression) expression).getLeftExpression().toString(); - String rightStr = ((OldOracleJoinBinaryExpression) expression).getRightExpression().toString(); - if (mappingMap.containsKey(leftStr)) { - mappingMap.get(leftStr).add(rightStr); - } else { - Set set = new HashSet<>(); - set.add(rightStr); - mappingMap.put(leftStr, set); - } - } - return expression; - } - - - //表之间的映射关系 - Map> tableMapping() { - Map> map = new HashMap<>(); - if (!CollectionUtils.isEmpty(mappingMap)) { - Set keys = mappingMap.keySet(); - for (String key : keys) { - Set set = new HashSet<>(); - Set values = mappingMap.get(key); - if (!CollectionUtils.isEmpty(values)) { - for (String value : values) { - set.add(replaceAliasName(value)); - } - } - map.put(replaceAliasName(key), set); - } - } - return map; - } - - //替换表别名 - private String replaceAliasName(String value) { - if (value.contains(".")) { - String[] target = value.split("[.]"); - return value.replace(target[0], tableMap.get(target[0])); - } - return value; - } - - - //将表按 别名:真实名 存进map中 - private void putTableMap(FromItem fromItem) { - String tableName = getTableName((Table) fromItem); - String tableAliasName = fromItem.getAlias().getName(); - tableMap.put(tableAliasName, tableName); - } - - //获取表名(包含 数据库名,表空间名) - private String getTableName(Table fromItem) { - String name = fromItem.getName(); //表名 - String databaseName = fromItem.getDatabase().getDatabaseName(); //数据库名 - String schemaName = fromItem.getSchemaName(); //表空间名 - if (Objects.isNull(databaseName) && !Objects.isNull(schemaName)) - return schemaName + "." + name; - if (!Objects.isNull(databaseName)) { - return databaseName + "." + (schemaName == null ? "" : schemaName) + "." + name; - } - return name; - } - - @Override - public void visit(SetOperationList sOperationList) { - //union all会用到 - List pSelects = sOperationList.getPlainSelects(); - for (PlainSelect pSelect : pSelects) { - this.visit(pSelect); - } - } - - @Override - public void visit(WithItem wItem) { -// System.out.println("WithItem=========" + wItem); - } -} diff --git a/src/main/java/com/luop/SelectParseHelper.java b/src/main/java/com/luop/SelectParseHelper.java index 999f3c6..f0f21dc 100644 --- a/src/main/java/com/luop/SelectParseHelper.java +++ b/src/main/java/com/luop/SelectParseHelper.java @@ -2,9 +2,12 @@ import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; +import net.sf.jsqlparser.statement.select.SetOperationList; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -27,10 +30,14 @@ static Map> getBloodRelationResult(String sqltxt) throws Exce //第三方插件解析sql Statement stmt = CCJSqlParserUtil.parse(sqltxt); //报错 说明sql语句错误 Select selectStatement=(Select)stmt; - - MySelectVisitor mySelectVisitor=new MySelectVisitor(); - SelectBody sBody=selectStatement.getSelectBody(); - sBody.accept(mySelectVisitor); - return mySelectVisitor.getSqlParseResult(); + MySelectVisitor mySelectVisitor = new MySelectVisitor(); + SelectBody sBody = selectStatement.getSelectBody(); + Map> columnsMappingMap = new HashMap<>(); + if (sBody instanceof SetOperationList) { + mySelectVisitor.visit((SetOperationList) sBody, columnsMappingMap); + } else { + mySelectVisitor.visit((PlainSelect) sBody, columnsMappingMap, false); + } + return columnsMappingMap; } } diff --git a/src/main/java/com/luop/SqlAnalysisApplication.java b/src/main/java/com/luop/SqlAnalysisApplication.java deleted file mode 100644 index 462c54c..0000000 --- a/src/main/java/com/luop/SqlAnalysisApplication.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.luop; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class SqlAnalysisApplication { - - public static void main(String[] args) { - SpringApplication.run(SqlAnalysisApplication.class, args); - } - -} diff --git a/src/test/java/com/luop/SqlAnalysisApplicationTests.java b/src/test/java/com/luop/SqlAnalysisApplicationTests.java index 591fefe..b89a843 100644 --- a/src/test/java/com/luop/SqlAnalysisApplicationTests.java +++ b/src/test/java/com/luop/SqlAnalysisApplicationTests.java @@ -1,20 +1,15 @@ package com.luop; + import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; import java.util.Map; import java.util.Set; -@SpringBootTest class SqlAnalysisApplicationTests { @Test - void contextLoads() { - } - - @Test - void test(){ + public void testPlainSql(){ String sql = "select u.name userName,u.id,d.name depName,d.id depId " + "from user u left join department d on u.depId = d.id where u.id = 1"; try { @@ -25,4 +20,38 @@ void test(){ } } + @Test + public void testSubQuerySql(){ + String sql = " SELECT t1.customer_id, t1.total_spent, t2.total_orders, date() as curdate " + + " FROM ( " + + " SELECT customer_id, SUM(order_total) AS total_spent " + + " FROM customer_summary " + + " GROUP BY customer_id " + + " HAVING total_spent > 1000 " + + " ) AS t1 " + + " JOIN ( " + + " SELECT customer_id, COUNT(DISTINCT order_id) AS total_orders " + + " FROM customer_summary " + + " GROUP BY customer_id " + + " " + + " ) AS t2 ON t1.customer_id = t2.customer_id;"; + try { + Map> map = SelectParseHelper.getBloodRelationResult(sql); + System.out.println(map); //{total_spent=[customer_summary.order_total], customer_id=[customer_summary.customer_id], total_orders=[customer_summary.order_id]} + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + public void testUnionSql(){ + String sql = "SELECT t1.customer_id as id, t1.order_total as money FROM customer1 t1 UNION ALL SELECT t2.customer_id as id, t2.order_total as money FROM customer2 t2"; + try { + Map> map = SelectParseHelper.getBloodRelationResult(sql); + System.out.println(map); //{money=[customer1.order_total, customer2.order_total], id=[customer1.customer_id, customer2.customer_id]} + } catch (Exception e) { + e.printStackTrace(); + } + } + }