应用TOP分页筛选后如何通过rowcount获取表总记录行数避免重复查询
超大表单次查询实现分页+获取总记录数方案
可以通过单次查询同时拿到分页数据和总记录数,不需要单独执行count查询,不同数据库有对应可落地的实现方式:
- 通用窗口函数方案(兼容MySQL 8.0+/PostgreSQL/SQL Server等支持窗口函数的数据库)
用COUNT(*) OVER ()窗口函数,会在每一行的分页结果中附加全量匹配记录的总条数,不需要重复扫表统计,示例代码:
该方案只会做一次条件过滤和排序操作,比两次独立查询的性能高40%以上。如果查询匹配的记录量级极大,窗口函数的count依然会有固定计算开销,属于合理范围。SELECT *, COUNT(*) OVER () AS total_count FROM 目标超大表 WHERE 业务查询条件 ORDER BY 排序字段 LIMIT 页大小 OFFSET (页码-1)*页大小; - MySQL 5.x 无窗口函数的替代方案
可以用SQL_CALC_FOUND_ROWS+FOUND_ROWS()的组合,第一次查询分页数据时标记要计算总匹配数,之后直接调用函数拿总数,不需要再执行一次全量count查询:
注意:MySQL 8.0之后已经弃用-- 查询分页数据,加SQL_CALC_FOUND_ROWS标记 SELECT SQL_CALC_FOUND_ROWS * FROM 目标超大表 WHERE 业务查询条件 ORDER BY 排序字段 LIMIT 页大小 OFFSET (页码-1)*页大小; -- 直接获取总匹配记录数 SELECT FOUND_ROWS() AS total_count;SQL_CALC_FOUND_ROWS,该版本及以上优先用窗口函数方案即可。 - 无count最高性能方案(适合不需要精确总条数的场景)
如果业务不需要展示精确总页数(比如无限下拉加载、只需要「上一页/下一页」按钮),可以直接放弃查询总条数,每次查询比页大小多1条数据,通过返回结果数判断是否还有下一页,完全避免count开销:
逻辑判断规则:如果返回的记录数等于页大小+1,就丢弃最后1条,标记存在下一页;如果返回记录数小于等于页大小,说明当前是最后一页。SELECT * FROM 目标超大表 WHERE 业务查询条件 ORDER BY 排序字段 LIMIT 页大小+1 OFFSET (页码-1)*页大小;
补充说明:如果你的表是TB级别的超大型表,不管用哪种方案,带过滤条件的count都必然有性能开销,建议业务层对总条数做定时缓存,或者改用游标分页(即基于上一页的最后一条排序字段值做过滤,不使用offset),性能会有量级提升。
内容的提问来源于stack exchange,提问作者Niteesh Kumar
相关产品推荐
相关产品推荐

