LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

SQL案例分析:窗口函数实现高效分页查询

admin
2024年3月15日 15:4 本文热度 524

我们在使用 SQL 语句实现分页查询时,需要知道一些额外的参数信息,例如查询返回的总行数、当前所在的页数、最后一页的页数等。

在传统的实现方法中我们需要执行额外的查询语句获得这些信息,本文介绍一种只需要一个查询语句就可以返回所有数据的方法,也就是通过 SQL 窗口函数实现高效的分页查询功能。

示例表和数据:https://github.com/dongxuyang1985/thinking_in_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|

📝关于窗口函数的介绍可以参考这篇文章

总结

本文介绍了如何利用窗口函数在一个语句中返回分页查询的结果和所需的全部参数,这种方法比传统的分页查询实现更加简洁高效。


该文章在 2024/3/15 15:04:21 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2024 ClickSun All Rights Reserved