APIJSON 博客8 AbstractSQLConfig 第八篇

2021SC@SDUSC

我们来继续上周的分析,下面是getRemoveString()方法,传入的参数是key和value

	@JSONField(serialize = false)
	public String getRemoveString(String key, Object value) throws IllegalArgumentException {
		if (value instanceof Number) {
			return getKey(key) + " - " + value;
		}
		if (value instanceof String) {
			return SQL.replace(getKey(key), (String) getValue(value), "''");// " replace(" + key + ", '" + value + "', '') ";
		}
		throw new IllegalArgumentException(key + "- 对应的值 " + value + " 不是Number,String,Array中的任何一种!");
	}

这里用到了 instanceof 对value进行了分类处理

value属于Number则return getKey(key) + " - " + value

value属于String则return SQL.replace(getKey(key), (String) getValue(value), "''")

这是对SQL中remove的处理

知识学习

remove:删除数据库文件

        alter database Mydatabase1

removefile Mydatabase1_log 

 下面是getSQL()方法

	@JSONField(serialize = false)
	@Override
	public String getSQL(boolean prepared) throws Exception {
		return getSQL(this.setPrepared(prepared));
	}
	
	public static String getSQL(AbstractSQLConfig config) throws Exception {
		if (config == null) {
			Log.i(TAG, "getSQL  config == null >> return null;");
			return null;
		}

		String sch = config.getSQLSchema();
		if (StringUtil.isNotEmpty(config.getProcedure(), true)) {
			String q = config.getQuote();
			return "CALL " + q + sch + q + "."+ config.getProcedure();
		}

		String tablePath = config.getTablePath();
		if (StringUtil.isNotEmpty(tablePath, true) == false) {
			Log.i(TAG, "getSQL  StringUtil.isNotEmpty(tablePath, true) == false >> return null;");
			return null;
		}

		switch (config.getMethod()) {
		case POST:
			return "INSERT INTO " + tablePath + config.getColumnString() + " VALUES" + config.getValuesString();
		case PUT:
			if(config.isClickHouse()){
				return  "ALTER TABLE " +  tablePath + " UPDATE"+ config.getSetString()+ config.getWhereString(true);
			}
			return "UPDATE " + tablePath + config.getSetString() + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : "");
		case DELETE:
			if(config.isClickHouse()){
				return  "ALTER TABLE " +  tablePath + " DELETE" + config.getWhereString(true);
			}
			return "DELETE FROM " + tablePath + config.getWhereString(true) + (config.isMySQL() ? config.getLimitString() : "");  
		default:
			String explain = (config.isExplain() ? (config.isSQLServer() || config.isOracle() ? "SET STATISTICS PROFILE ON  " : "EXPLAIN ") : "");
			if (config.isTest() && RequestMethod.isGetMethod(config.getMethod(), true)) {
				String q = config.getQuote();  // 生成 SELECT  (  (24 >=0 AND 24 <3)  )  AS `code` LIMIT 1 OFFSET 0
				return explain + "SELECT " + config.getWhereString(false) + " AS " + q + JSONResponse.KEY_CODE + q + config.getLimitString();
			}

			config.setPreparedValueList(new ArrayList<Object>());
			String column = config.getColumnString();
			if (config.isOracle()) {
				
				if ((config.getMethod() == HEAD || config.getMethod() == HEADS)
						&& StringUtil.isNotEmpty(config.getGroup(),true)){
					return explain + "SELECT count(*) FROM (SELECT "+ (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + ") " + config.getLimitString();
				}
				return explain + "SELECT * FROM (SELECT "+ (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + ") " + config.getLimitString();
			}
			
			return explain + "SELECT " + (config.getCache() == JSONRequest.CACHE_RAM ? "SQL_NO_CACHE " : "") + column + " FROM " + getConditionString(column, tablePath, config) + config.getLimitString();
		}
	}

procedure 改为 List<Procedure>  procedureList;

behind : true; function: callFunction(); String key; 

for (...) { Call procedure1();\n SQL \n; Call procedure2(); ... }
貌似不需要,因为 ObjecParser 里就已经处理的顺序等,只是这里要解决下 Schema 问题。

首先是get一个tablePath,使用config.getMethod()获取使用的SQL方法,根据所的方法返回一个sql语句如"ALTER TABLE " +  tablePath + " UPDATE"+ config.getSetString()+ config.getWhereString(true),包含了SELECT,DELETE,INSERT,UPDATE等方法

同时注意PostgreSQL 不允许 LIMIT 

MYSQL的LIMIT用法如下

sql中limit的用法

mysql

limit子句用于限制查询结果返回的数量。

用法:【select * from tableName limit i,n 】

参数:

tableName : 为数据表;
i : 为查询结果的索引值(默认从0开始);
n : 为查询结果返回的数量
示例:
1)查询student的数据:

select * from student

 

2)查询第一条数据

select * from student limit 1
执行结果如下图所示:

 

3)查询第二条数据

select * from student limit 1,1
执行结果如下图所示:

 

当配置的数据库是oracle时,使用子查询,因为下面的Oracle12不支持偏移量获取分页语法。
针对oracle分组后条数的统计。

下面是一个private的方法getConditionString()

private static String getConditionString(String column, String table, AbstractSQLConfig config) throws Exception {
		String where = config.getWhereString(true);

		Subquery from = config.getFrom();
		if (from != null) {
			table = config.getSubqueryString(from) + " AS " + config.getAliasWithQuote() + " ";
		}

		
		String aggregation = "";
		if (RequestMethod.isGetMethod(config.getMethod(), true)){
			aggregation = config.getGroupString(true) + config.getHavingString(true) +
					config.getOrderString(true);
		}
		if (RequestMethod.isHeadMethod(config.getMethod(), true)){
			aggregation = config.getGroupString(true) + config.getHavingString(true) ;
		}
		if (config.getMethod() == PUT || config.getMethod() == DELETE){
			aggregation = config.getHavingString(true) ;
		}

		String condition = table + config.getJoinString() + where + aggregation;
				return condition; 
}

根据方法不同,聚合语句不同。GROUP  BY 和 HAVING 可以加在 HEAD 上, HAVING 可以加在 PUT, DELETE 上,GET 全加,POST 全都不加

return的condition语句是由table、config.getJoinString、where、aggregation组成

table由调用方法时传入的参数决定,当table为空时,table赋值为config.getSubqueryString(from) + " AS " + config.getAliasWithQuote() + " "  意为form的子查询+AS+AliasWithQuote(带引号的别名),where是whereString,aggregation是根据方法不同确定的OrderString/GroupString/HavingString。最终返回的SQL条件语句。

下面是getJoinString()方法

	public String getJoinString() throws Exception {
		String joinOns = "";

		if (joinList != null) {
			String quote = getQuote();
			List<Object> pvl = new ArrayList<>();
			boolean changed = false;

			String sql = null;
			SQLConfig jc;
			String jt;
			String tt;

			for (Join j : joinList) {
				if (j.isAppJoin()) { 
					continue;
				}
				String type = j.getJoinType();

			
				jc = j.getJoinConfig();
				jc.setPrepared(isPrepared());

				jt = StringUtil.isEmpty(jc.getAlias(), true) ? jc.getTable() : jc.getAlias();
				tt = j.getTargetTable();



				switch (type) {
	
				case "*": // CROSS JOIN
					onGetCrossJoinString(j);
				case "<": // LEFT JOIN
				case ">": // RIGHT JOIN
					jc.setMain(true).setKeyPrefix(false);
					sql = ( "<".equals(type) ? " LEFT" : (">".equals(type) ? " RIGHT" : " CROSS") )
							+ " JOIN ( " + jc.getSQL(isPrepared()) + " ) AS "
							+ quote + jt + quote + " ON " + quote + jt + quote + "." + quote + j.getKey() + quote + " = "
							+ quote + tt + quote + "." + quote + j.getTargetKey() + quote;
					jc.setMain(false).setKeyPrefix(true);

					pvl.addAll(jc.getPreparedValueList());
					changed = true;
					break;

				case "&": // INNER JOIN: A & B 
				case "":  // FULL JOIN: A | B 
				case "|": // FULL JOIN: A | B 
				case "!": // OUTER JOIN: ! (A | B)
				case "^": // SIDE JOIN: ! (A & B)
				case "(": // ANTI JOIN: A & ! B
				case ")": // FOREIGN JOIN: B & ! A
					sql = " INNER JOIN " + jc.getTablePath()
					+ " ON " + quote + jt + quote + "." + quote + j.getKey() + quote + " = " + quote + tt + quote + "." + quote + j.getTargetKey() + quote;
					break;
				default:
					throw new UnsupportedOperationException(
							"join:value 中 value 里的 " + jt + "/" + j.getPath()
							+ "错误!不支持 " + jt + " 等 [ @ APP, < LEFT, > RIGHT, * CROSS"
							+ ", & INNER, | FULL, ! OUTER, ^ SIDE, ( ANTI, ) FOREIGN ] 之外的 JOIN 类型 !"
							);
				}

				joinOns += "  \n  " + sql;
			}


			if (changed) {
				pvl.addAll(preparedValueList);
				preparedValueList = pvl;
			}

		}

		return joinOns;
	}

条件中的APP JOIN,只是作为一个标记,执行完主表的查询后自动执行副表的查询 User.id IN($commentIdList)

首先是一个增强for循环for (Join j : joinList)

增强for循环

创建一个for循环,但是循环条件和普通for循环条件不同。

for(int num : a)

条件中首先定义一个变量整型变量num,这个变量类型的和数组存放的类型是一样的。这里的话是用来表示数组a中的值,然后底层的话会把数组a中的值一个一个的赋值。

 该方法先是定义了一个joinOns,joinOns是前面定义的String SQL添加而来,这里用了一个switch (type){case}来根据不同的join TYPE设置不同的SQL,加入到jionOns。对joinlist里面的每个join j都进行了SQL的设定,累加到JIONONS里面,最后return。

这里提到了SQL的JOIN方法

  • INNER JOIN:如果表中有至少一个匹配,则返回行
  • LEFT JOIN:即使右表中没有匹配,也从左表返回所有的行
  • RIGHT JOIN:即使左表中没有匹配,也从右表返回所有的行
  • FULL JOIN:只要其中一个表中存在匹配,则返回行
  • 这些是常见的JOIN类型

额外提到了以下JOIN类型

SIDE JOIN: ! (A & B):不包括A&B的部分
ANTI JOIN: A & ! B:A和B没有的部分
FOREIGN JOIN: B & ! A:B和A没有的部分

 

	protected void onGetCrossJoinString(Join j) throws UnsupportedOperationException {
		throw new UnsupportedOperationException("已禁用 * CROSS JOIN !性能很差、需求极少,如要取消禁用可在后端重写相关方法!");
	}

这里禁用了CROSS JOIN,原因是性能很差、需求极少

下面是CROSSJOIN的用法

CROSS JOIN又称为笛卡尔乘积,实际上是把两个表乘起来。

SQL CROSS JOIN will return all records where each row from the first table is combined with each row from the second table. Which also mean CROSS JOIN returns the Cartesian product of the sets of rows from the joined tables.

CROSS JOIN can be specified in two ways: using the JOIN syntax or by listing the tables in the FROM clause separated by commas without using a WHERE clause to supply join criteria.

SQL CROSS JOIN syntax:

SELECT * FROM [TABLE 1] CROSS JOIN [TABLE 2]

OR

SELECT * FROM [TABLE 1], [TABLE 2]


EXAMPLE :

Let's try with 2 tables below:

Table 1: GameScores

PlayerNameDepartmentIdScores
Jason13000
Irene11500
Jane21000
David22500
Paul32000
James32000

Table 2: Departments

DepartmentIdDepartmentName
1IT
2Marketing
3HR

SQL statement :

SELECTFROM GameScores CROSS JOIN Departments

Result:

PlayerNameDepartmentIdScoresDepartmentIdDepartmentName
Jason130001IT
Irene115001IT
Jane210001IT
David225001IT
Paul320001IT
James320001IT
Jason130002Marketing
Irene115002Marketing
Jane210002Marketing
David225002Marketing
Paul320002Marketing
James330002Marketing
Jason130003HR
Irene115003HR
Jane210003HR
David225003HR
Paul320003HR
James330003HR

 2870

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值