You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单次SQL查询中同时实现分页与总记录数统计?

解决分页查询同时获取总记录数的问题

这个问题我之前也遇到过!你原来的写法之所以不行,是因为聚合函数count(*)和普通的select *混用的时候,数据库不知道该怎么处理——不加GROUP BY的话,count(*)会把全表合并成一行统计,而select *是要返回多行数据,两者逻辑冲突,自然跑不起来。下面给你几个可行的解决办法:

方法一:执行两个独立查询(最常用、最可靠)

这是实际开发中最常用的方案,逻辑简单,也容易维护。先单独查询总记录数,再查询分页数据:

-- 第一步:获取总记录数(用于计算总页数)
SELECT COUNT(*) AS total FROM `product`;

-- 第二步:获取第1页的分页数据(每页20条)
SELECT * FROM `product` LIMIT 20 OFFSET 0;

这种方式的优势在于,数据库优化器对这两个查询都能很好地处理,尤其是当product表有合适的索引时,COUNT(*)的执行速度会非常快。绝大多数ORM框架(比如MyBatis、Hibernate)的分页功能,底层都是用这种方式实现的。

方法二:用子查询将总记录数嵌入每一行

如果你的业务场景要求必须一次查询返回所有需要的数据,可以用子查询把总记录数作为一个额外字段返回:

SELECT 
  p.*, 
  (SELECT COUNT(*) FROM `product`) AS total
FROM `product` p
LIMIT 20 OFFSET 0;

这样返回的每一条分页数据都会带上total字段,虽然有点冗余,但能满足“一次查询拿到所有数据”的需求。需要注意的是,如果表的数据量特别大,COUNT(*)可能会有性能损耗,这时候如果业务允许,可以考虑用近似值(比如MySQL中通过EXPLAIN获取预估行数),但如果需要精确值,还是得用COUNT(*)。

方法三:用窗口函数(适合支持窗口函数的数据库)

如果你的数据库版本支持窗口函数(比如MySQL 8.0+、PostgreSQL、SQL Server等),可以用更优雅的写法:

SELECT *, COUNT(*) OVER() AS total
FROM `product`
LIMIT 20 OFFSET 0;

窗口函数COUNT(*) OVER()会在不聚合整个结果集的前提下,计算全表的总记录数,然后将这个值附加到每一行上。数据库会自动优化这个查询,不需要额外执行两次查询,写法也更简洁。

额外提示:优化大偏移量的分页性能

如果你的数据量很大,使用OFFSET进行分页可能会越来越慢(比如OFFSET 10000这样的查询,数据库需要先跳过前10000条数据)。这时候可以考虑用基于主键的分页,比如:

-- 假设product表有自增主键id,上一页最后一条数据的id是last_id
SELECT * FROM `product` WHERE id > last_id LIMIT 20;

这种方式的性能会比OFFSET好很多,因为数据库可以直接通过主键索引定位到起始位置。

内容的提问来源于stack exchange,提问作者Renato Souza de Oliveira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:50:23