← 中文资源

简体中文版本

偏移分页与窗口计数

实现经典偏移分页:COUNT 窗口函数与数据同查询取回免二次往返,页大小与翻页深度设硬上限防御性约束。

阅读英文原文

示例说明

偏移分页的两个痛点:总数需要额外 COUNT 查询(两次往返);参数无上限会被恶意构造(LIMIT 十万或 OFFSET 二十亿拖垮库)。COUNT(*) OVER () 窗口函数把总数附着在每行上——单次查询带回数据与元数据;上限约束在生成 SQL 的应用层强制执行。

技术要点

  • 窗口 COUNT 免去独立总数查询
  • 页大小与深度双重硬上限
  • 整数除法直接算总页数

代码解读

主查询的 COUNT(*) OVER () 不带分区——对全部匹配行计数并复制到每行,应用层从首行取值;total_pages 用整数除法加每页大小减一向上取整。注释里明确两道防线:单页不超一百条、偏移不超一万——超出走键集分页或搜索引擎。

使用建议

COUNT OVER 会让部分执行计划物化全部行,大数据量拆两次查询反而更快。总数展示可以缓存或异步。管理后台之外的接口尽量用游标分页。

代码示例

-- 单次往返:数据页与总数用窗口函数一并取回
SELECT
  id,
  name,
  email,
  status,
  COUNT(*) OVER () AS total_matching
FROM customers
WHERE status = 'active'
ORDER BY created_at DESC, id DESC
LIMIT 25 OFFSET 50;

-- 分页参数带硬上限(防恶意大页与深翻页)
-- 应用层生成的 SQL 遵循:
--   page_size <= 100
--   page_number * page_size <= 10000
SELECT id, name
FROM customers
ORDER BY id
LIMIT 100 OFFSET 0;

-- 跳转特定页(第 4 页,每页 25 条)
SELECT
  id, name,
  COUNT(*) OVER () AS total,
  (COUNT(*) OVER () + 24) / 25 AS total_pages
FROM customers
ORDER BY id
LIMIT 25 OFFSET 75;