# sql-paging **Repository Path**: tenmg/sql-paging ## Basic Information - **Project Name**: sql-paging - **Description**: SQL分页查询方言类库。通过调用相关API,可快速将一个普通SQL转换为一个特定数据库的计数(COUNT)SQL或分页查询SQL。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 16 - **Forks**: 5 - **Created**: 2021-07-20 - **Last Updated**: 2025-02-10 ## Categories & Tags **Categories**: database-dev **Tags**: SQL ## README # sql-paging

maven

## 介绍 sql-paging是一个SQL分页查询方言类库,它原来是[Sqltool](https://gitee.com/tenmg/sqltool)的智能分页组件,后剥离出来作为独立项目,以供更多组件集成其能力。通过调用相关API,可快速将一个普通SQL转换为一个特定数据库的计数(`COUNT`)SQL或分页查询SQL。sql-paging通过内置的SQL分析工具类分析实际调用的SQL,让方言生成最优的计数(`COUNT`)SQL或分页查询SQL。 ## 数据库支持 数据库 | 支持版本 | 方言实现类 -----------|---------|------------------------- MySQL | 1.0+ | MySQLPagingDialect Oracle | 1.0+ | OraclePagingDialect PostgreSQL | 1.0+ | PostgreSQLPagingDialect SQLServer | 1.0+ | SQLServerPagingDialect SQLite | 1.2.7+ | SQLitePagingDialect ## 使用说明 以基于Maven项目为例 1. pom.xml添加依赖,${sql-paging.version}为版本号,可定义属性或直接使用版本号替换 ``` cn.tenmg sql-paging ${sql-paging.version} ``` 2. 调用`SQLPagingDialect.countSql`方法获取计数SQL(以MySQL数据库为例) ``` String namedSql = "……"; sqlMetaData sqlMetaData = SQLUtils.getSQLMetaData(namedSql); SQLPagingDialect dialect = MySQLPagingDialect.getInstance(); String countSql = dialect.countSql(namedSql, sqlMetaData); …… ``` 3. 调用`SQLPagingDialect.pageSql`方法获取分页查询SQL(以MySQL数据库为例) ``` …… try { String pageSql = dialect.pageSql(con, namedSql, params, sqlMetaData, 20, 2); …… } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } …… ``` ## API详解 ### countSql 用于根据实际查询的SQL自动生成计数SQL,完成对总数的统计,结合页容量可计算出总页数。根据对源SQL的分析和智能决策,生成计数SQL会去除不必要的列或者排序子句(ORDER BY),且不会引入不必要子查询,以达到最优性能。例如如下SQL: ``` SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID ``` 并不是简单包裹子查询实现计数: ``` SELECT COUNT(*) FROM ( SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID ) T ``` 而是,不嵌套不必要的子查询,并去除不必要的排序子句: ``` SELECT COUNT(*) FROM STAFF_INFO S ``` 嗯,这的确是我们想要的样子。但如果情况复杂一点呢?比如,我们需要查询某段时间内用户的订单金额并按金额从大到小排序: ``` SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ORDER BY SUM(AMT) DESC ``` 我们得到的是: ``` SELECT COUNT(*) FROM ( SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ) SQL_PAGING ``` 干得漂亮!这完全是我们所期待的。但如果情况再复杂一点呢?比如这样,我们需要查询某段时间内订单金额前一百名的用户: ``` SELECT USER_ID, /*用户编号*/ AMT /*订单金额*/ FROM ( SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ) T ORDER BY AMT DESC LIMIT 100 ``` 我们得到的是: ``` SELECT COUNT(*) FROM ( SELECT USER_ID, /*用户编号*/ AMT /*订单金额*/ FROM ( SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end ) T LIMIT 100 ) SQL_PAGING ``` sql-paging没有误杀无辜者,除了去除 `ORDER BY` 子句之外,其他保留原样,保证了结果正确性的同时,提升了查询效率。 ### pageSql 用于根据实际查询的SQL生成分页查询SQL,它也不是简单地对源SQL包裹子查询,同样是按需智能决策。继续上述三个例子: 1. ``` SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID ``` 得到的分页查询SQL(以页容量为10,页码第2页为例): 1.1. MySQL ``` SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID LIMIT 10,10 ``` 1.2. Oracle ``` SELECT STAFF_ID, STAFF_NAME, DEPARTMENT_ID, POSITION, STATUS FROM ( SELECT ROWNUM RN__, SQL_PAGING.* FROM ( SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID ) SQL_PAGING WHERE RN__ <= 20 ) WHERE RN__ > 10 ``` 1.3. PostgresSQL ``` SELECT S.STAFF_ID, S.STAFF_NAME, S.DEPARTMENT_ID, S.POSITION, S.STATUS FROM STAFF_INFO S ORDER BY S.STAFF_ID LIMIT 10 OFFSET 10 ``` 2. ``` SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ORDER BY SUM(AMT) DESC ``` 得到的分页查询SQL(以页容量为10,页码第2页为例): 2.1. MySQL: ``` SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ORDER BY SUM(AMT) DESC LIMIT 10,10 ``` 2.2. Oracle ``` SELECT USER_ID, AMT FROM ( SELECT ROWNUM RN__, SQL_PAGING.* FROM ( SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ORDER BY SUM(AMT) DESC ) SQL_PAGING WHERE RN__ <= 20 ) WHERE RN__ > 10 ``` 2.3. PostgresSQL ``` SELECT USER_ID, SUM(AMT) AMT FROM ORDER_INFO O WHERE O.CREATE_TIME >= :begin AND O.CREATE_TIME < :end GROUP BY USER_ID ORDER BY SUM(AMT) DESC LIMIT 10 OFFSET 10 ``` ## 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request ## 相关链接 DSL开源地址:https://gitee.com/tenmg/dsl