3. 使用ORDER BY与LIMIT实现分页查询

使用ORDER BY与LIMIT实现分页查询 学习卡片

排序键与分页参数的关系

分页查询的核心不是“取第几页”,而是先确定一个稳定且唯一的排序顺序,再用 LIMIT 截取行数、用 OFFSET 跳过前面页的行。本节按“薪资降序 + 入职时间升序”排序,并额外加入 emp_id 作为最终决胜键,避免薪资和入职时间都相同的员工在不同页之间漂移。

排序 → 分页切片关系 ORDER BY 排序键 ① salary DESC ② hire_date ASC ③ emp_id ASC(决胜键) 保证全表顺序唯一、可复现 已排序结果集 行1 张伟 28000 2021-03-01 行2 李娜 26000 2020-07-15 行3 王强 26000 2022-01-10 行4 赵敏 24000 2019-11-20 行5 陈晨 22000 2023-05-06 LIMIT / OFFSET 切片 第1页:LIMIT 2 OFFSET 0 第2页:LIMIT 2 OFFSET 2 第3页:LIMIT 2 OFFSET 4 OFFSET = (页码-1) × 每页行数 分页边界与结果判读 第1页 OFFSET 0 返回 2 行 张伟、李娜 第2页 OFFSET 2 返回 2 行 王强、赵敏 第3页 OFFSET 4 返回 1 行 陈晨(不足一页) 越界页 OFFSET 6 返回 0 行 空结果,非报错

建表与示例数据

沿用员工表结构,插入 5 条覆盖不同薪资与入职时间的记录,便于观察排序与分页边界。

CREATE TABLE employee (
  emp_id     INT PRIMARY KEY,
  emp_name   VARCHAR(20) NOT NULL,
  dept       VARCHAR(20),
  salary     DECIMAL(10,2),
  hire_date  DATE
);

INSERT INTO employee (emp_id, emp_name, dept, salary, hire_date) VALUES
(1, '张伟', '研发部', 28000.00, '2021-03-01'),
(2, '李娜', '研发部', 26000.00, '2020-07-15'),
(3, '王强', '市场部', 26000.00, '2022-01-10'),
(4, '赵敏', '财务部', 24000.00, '2019-11-20'),
(5, '陈晨', '市场部', 22000.00, '2023-05-06');

排序与分页查询

先按薪资降序,薪资相同时按入职时间升序(早入职者在前),最后用 emp_id 升序兜底。每页 2 行,分别取第 1、2、3 页。

-- 第1页
SELECT emp_id, emp_name, salary, hire_date
FROM employee
ORDER BY salary DESC, hire_date ASC, emp_id ASC
LIMIT 2 OFFSET 0;

-- 第2页
SELECT emp_id, emp_name, salary, hire_date
FROM employee
ORDER BY salary DESC, hire_date ASC, emp_id ASC
LIMIT 2 OFFSET 2;

-- 第3页
SELECT emp_id, emp_name, salary, hire_date
FROM employee
ORDER BY salary DESC, hire_date ASC, emp_id ASC
LIMIT 2 OFFSET 4;

结果与边界验证

页码OFFSET返回行数返回员工说明
第1页02张伟、李娜满页
第2页22王强、赵敏满页
第3页41陈晨不足一页,正常
越界页60—空结果,不报错
李娜与王强薪资同为 26000,但李娜入职更早(2020-07-15 < 2022-01-10),因此排在王强之前。若只按 salary DESC 排序,两人的相对顺序不确定,分页时可能重复或漏行。
OFFSET 越大,数据库需要先扫描并丢弃前面的行,深分页性能会下降。生产环境可改用“基于游标的分页”(如 WHERE salary < 上一页末行薪资)替代大 OFFSET。

可复用分页检查清单

  • 排序键是否包含唯一列(如主键)作为最终决胜键?
  • OFFSET 是否等于 (页码 - 1) × 每页行数?
  • 是否验证了最后一页不足一页、以及越界页返回 0 行的情况?
  • 翻页时排序字段与方向是否保持一致?