背景
公司有一个分页的后台组件,为了方便开发人员使用,在让开发人员传分页语句时候,不需要传入求总数的sql语句,那么这样就会有个埋伏,当查询语句时普通查询语句时没有问题,如果查询语句是带有排序的语句,并且sql数据量相对较大的情况下,会在本身查询时不慢,但是在求总数时特别慢。
那么解决上述问题一共有几种办法。
- 把分页求总数的语句给单独起个属性给暴露给开发人员。(这样,开发人员的复杂性就大了)
- 利用字符串判断order by 的位置,然后截取,(如何保证sql不被截取错误)
- 用正则表达式去除最后面的Order By sql子句(多个子查询潜逃的情况下,可能会被删除,导致执行错误)
- 解析sql语法,只去除最外层sql的order by,可用工具有(JSqlParser,Apache Calcite,JavaCC,druid)
最后采用第四种方案处理这个问题,下面分别解释各个sql解析工具的利弊。
工具 | 描述 | 优点 | 缺点 | 厂商 |
---|---|---|---|---|
JSqlParser | 基于JavaCC实现的sql解析工具 | 支持多数据库,独立 | 部分数据库支持得不是很好 | |
Apache Calcite | 基于Javacc实现的Sql解析工具 | 支持多数据库,独立 | apache顶级开源项目 | |
druid | 国内阿里巴巴开源的数据库连接池。其中包含的有sql解析的工具 | 支持语言多,解析的AST树复杂 | 跟数据库连接池耦合在一起,无法拆开使用 | 阿里巴巴 |
JavaCC | 语法解析工具 | |||
antlr | 语法解析工具 |
使用jsqlparse处理的方式,去除最外层sql包含的order by
CCJSqlParserManager pm = new CCJSqlParserManager();
String countquerySql = querysql;
try{
Statement parse = pm.parse(new StringReader(querysql));
Select noOrderSelect = (Select)parse;
SelectBody selectBody = noOrderSelect.getSelectBody();
PlainSelect setOperationList = (PlainSelect)selectBody;
if (null!=setOperationList.getOrderByElements()){
setOperationList.setOrderByElements(null);
};
countquerySql = noOrderSelect.toString();
if(logger.isDebugEnabled()){
logger.debug("Delete the last order by statement with jsqlparse , sql:{}",countquerySql);
}
}catch (JSQLParserException e){
//throw new DbException("sql parse is err (remove count order by) , sql is : "+querysql);
logger.warn("sql parse is err(remove count order by) origin sql:{}, fix sql:{}",querysql,countquerySql);
countquerySql = querysql;
}
使用druid去除最外层sql中的order by ,后面补充
使用Calcite去除最外层sql的的orderby,后面补充
最后采用jsqlparse,原因是之前采用过