sql语句实现分页(SQL 窗口函数实现高效分页查询的案例分析)
sql语句实现分页
SQL 窗口函数实现高效分页查询的案例分析
🍺不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之。学至于行之而止矣。——荀子
大家好!我是只谈技术不剪发的 Tony 老师。
在使用 SQL 语句实现分页查询时,我们需要知道一些额外的参数信息,例如查询返回的总行数、当前所在的页数、最后一页的页数等。在传统的实现方法中我们需要执行额外的查询语句获得这些信息,本文介绍一种只需要一个查询语句就可以返回所有数据的方法,也就是通过 SQL 窗口函数实现高效的分页查询功能。
本文使用的示例表和数据可以这里下载。
传统方法实现分页查询
在 SQL 中实现分页查询的传统方法就是利用标准的 OFFSET … FETCH 语句或者许多数据库支持的 LIMIT … OFFSET 语句,例如:
-- Oracle、SQL Server、PostgreSQL SELECT emp_name, sex, email FROM employee ORDER BY emp_id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; -- MySQL、PostgreSQL、SQLite SELECT emp_name, sex, email FROM employee ORDER BY emp_id LIMIT 10 OFFSET 10;
以上语句非常容易理解,返回的是第 2 页中的 10 条记录。但是问题在于我们如何知道总共包含多少页数据(或者总的记录数),显然在此之前我们需要执行另一个查询:
SELECT COUNT(*) FROM employee; COUNT(*)| --------+ 25|
有了总的记录数 25 之后,我们可以计算出数据总共有 3 页,每页 10 条。
这种方法要求我们每次进行分页查询时都需要执行 2 个查询语句,使用起来不是很方便。下面我们介绍更加高效的窗口函数分页查询。
📝关于分页查询的实现,OFFSET 分页对于大量数据的分页可能存在性能问题,另一种方法就是采用键集分页(keyset pagination)。
窗口函数实现分页查询
首先让我们考虑一下使用 OFFSET 分页查询时需要哪些参数:
- TOTAL_ROWS,总记录数;
- CURRENT_PAGE,当前所在页码;
- MAX_PAGE_SIZE,每一页最多显示的记录数,例如 10、20、50;
- ACTUAL_PAGE_SIZE,当前页实际包含的记录数;
- ROW_NBR,每条记录的实际偏移量;
- LAST_PAGE,当前页是否是最后一页。
每一页最多显示的记录数(MAX_PAGE_SIZE)是我们传递给数据库的参数,其他则是查询返回的结果,我们可以通过下面的查询语句实现所有的功能:
-- Oracle、SQL Server、PostgreSQL WITH e AS ( -- 初始查询 SELECT emp_id, emp_name, sex, email FROM employee ), t AS ( SELECT emp_id, emp_name, sex, email, COUNT(*) OVER () AS total_rows, -- 总记录数 ROW_NUMBER () OVER (ORDER BY e.emp_id) AS row_nbr -- 偏移量,ORDER BY和初始查询相同 FROM e ORDER BY e.emp_id -- 排序 OFFSET 10 ROWS -- 分页 FETCH NEXT 10 ROWS ONLY ) SELECT emp_id, emp_name, sex, email, COUNT(*) OVER () AS actual_page_size, -- 当前页实际记录数 CASE MAX(row_nbr) OVER () WHEN total_rows THEN 'Y' ELSE 'N' END AS last_page, -- 是否最后一页 total_rows, -- 总记录数 row_nbr, -- 每一条数据的偏移量 ((row_nbr - 1) / 10) + 1 AS current_page -- 当前所在页码 FROM t ORDER BY emp_id; -- MySQL、PostgreSQL、SQLite WITH e AS ( -- 初始查询 SELECT emp_id, emp_name, sex, email FROM employee ), t AS ( SELECT emp_id, emp_name, sex, email, COUNT(*) OVER () AS total_rows, -- 总记录数 ROW_NUMBER () OVER (ORDER BY e.emp_id) AS row_nbr -- 偏移量,ORDER BY和初始查询相同 FROM e ORDER BY e.emp_id -- 排序 LIMIT 10 OFFSET 10 ROWS -- 分页 ) SELECT emp_id, emp_name, sex, email, COUNT(*) OVER () AS actual_page_size, -- 当前页实际记录数 CASE MAX(row_nbr) OVER () WHEN total_rows THEN 'Y' ELSE 'N' END AS last_page, -- 是否最后一页 total_rows, -- 总记录数 row_nbr, -- 每一条数据的偏移量 ((row_nbr - 1) / 10) + 1 AS current_page -- 当前所在页码 FROM t ORDER BY emp_id;
首先,我们定义了通用表表达式 e,它是返回数据的初始查询,可以增加其他的过滤条件。
然后,我们基于 e 定义了另一个通用表表达式 t,在定义中进行了排序和分页,并且利用窗口函数 COUNT(*) 计算总的记录数,利用窗口函数 ROW_NUMBER () 计算每条数据的偏移量(行号)。
接下来,我们基于 t 返回了更多的参数,利用窗口函数 COUNT(*) 返回了当前页的实际记录数,通过窗口函数 MAX(row_nbr) 返回的当前页最大偏移量和总记录数的比较判断是否最后一页,以及当前所在的页码。
emp_id|emp_name|sex|email |actual_page_size|last_page|total_rows|row_nbr|current_page| ------+--------+---+-------------------+----------------+---------+----------+-------+------------+ 11|关平 |男 |guanping@shuguo.com| 10|N | 27| 11| 2| 12|赵氏 |女 |zhaoshi@shuguo.com | 10|N | 27| 12| 2| 13|关兴 |男 |guanxing@shuguo.com| 10|N | 27| 13| 2| 14|张苞 |男 |zhangbao@shuguo.com| 10|N | 27| 14| 2| 15|赵统 |男 |zhaotong@shuguo.com| 10|N | 27| 15| 2| 16|周仓 |男 |zhoucang@shuguo.com| 10|N | 27| 16| 2| 17|马岱 |男 |madai@shuguo.com | 10|N | 27| 17| 2| 18|法正 |男 |fazheng@shuguo.com | 10|N | 27| 18| 2| 19|庞统 |男 |pangtong@shuguo.com| 10|N | 27| 19| 2| 20|蒋琬 |男 |jiangwan@shuguo.com| 10|N | 27| 20| 2|
📝关于窗口函数的介绍可以参考这篇文章。
总结
本文介绍了如何利用窗口函数在一个语句中返回分页查询的结果和所需的全部参数,这种方法比传统的分页查询实现更加简洁高效。
- mysql的视图和临时表区别(MySQL 内存表和临时表的用法详解)
- mpp数据库的客户端工具(xampp默认mysql数据库root密码的修改)
- sql server查看之前的代码(SQL Server简单实现数据的日报和月报功能)
- dedecms怎么设置栏目(DEDECMS实现在标签中嵌套调用SQL的方法)
- mysql出现锁表的原因(导致MySQL做全表扫描的几种情况)
- sql语句left+join详解(SQL语句中JOIN的用法场景分析)
- 怎么把csv文件导入mysql(mysql导入csv的4种报错的解决方法)
- sql server 2008自定义函数(SQL SERVER 2012新增函数之逻辑函数CHOOSE详解)
- sql分区原理(分区表场景下的 SQL 优化)
- mysql 用户权限配置(详解MySQL 用户权限管理)
- sql server支持两种登录验证方式(远程登陆SQL Server 2014数据库的方法)
- python调用excel教程(利用python在excel里面直接使用sql函数的方法)
- SQLServer数据库中开启CDC导致事务日志空间被占满的原因(SQLServer数据库中开启CDC导致事务日志空间被占满的原因)
- sql server 进阶教程(SQL Server游标的介绍与使用)
- mysql密码过期怎么改(年底了,你的mysql密码安全吗)
- SQL Server ltrimrtrim函数的用法(SQL Server ltrimrtrim 去不掉空格的原因分析)
- 贾怀胤唱《白龙马》 炸场 了 没想到京剧还能这么玩(贾怀胤唱白龙马)
- 白龙马的改编学生版,快来看看(白龙马的改编学生版)
- 萌娃唱《白龙马》走红,那生动的小表情,网友直呼 简直是戏精(萌娃唱白龙马走红)
- 朱鹤松被不断认可,凤凰传奇玲花喊话岳云鹏,索要老朱演出门票(朱鹤松被不断认可)
- 元宵晚会槽点多,芒果台上来就假唱,岳云鹏不说相声改评书了(元宵晚会槽点多)
- 岳云鹏跟凤凰传奇谈心,说出了人生中最重要的三个人,这才成功(岳云鹏跟凤凰传奇谈心)
热门推荐
- dedecms悬浮导航(DEDECMS开启绝对地址PC端跳转到静态手机端实现方法)
- dedecms添加代码(dedecms内容页调用栏目链接和栏目名称的方法)
- vue用手动上传图片(vue上传图片文件的多种实现方法)
- laravel使用教程(基于Laravel5.4版本的基本增删改查操作方法)
- xampp数据库表在哪个文件夹(XAMPP集成环境中MySQL数据库的使用)
- mvc中Scripts.Render、Styles.Render
- MySql开启远程连接
- python对于gbk处理(Python 2/3下处理cjk编码的zip文件的方法)
- 腾讯云服务器控制台搭建(手把手教你搭建腾讯云服务器入门图文教程)
- idea连接不上docker(IDEA使用Docker插件远程部署项目到云服务器的方法步骤)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9