3. 使用ORDER BY与LIMIT实现分页查询
排序键与分页参数的关系
分页查询的核心不是“取第几页”,而是先确定一个稳定且唯一的排序顺序,再用 LIMIT 截取行数、用 OFFSET 跳过前面页的行。本节按“薪资降序 + 入职时间升序”排序,并额外加入 emp_id 作为最终决胜键,避免薪资和入职时间都相同的员工在不同页之间漂移。
建表与示例数据
沿用员工表结构,插入 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页 | 0 | 2 | 张伟、李娜 | 满页 |
| 第2页 | 2 | 2 | 王强、赵敏 | 满页 |
| 第3页 | 4 | 1 | 陈晨 | 不足一页,正常 |
| 越界页 | 6 | 0 | — | 空结果,不报错 |
李娜与王强薪资同为 26000,但李娜入职更早(2020-07-15 < 2022-01-10),因此排在王强之前。若只按 salary DESC 排序,两人的相对顺序不确定,分页时可能重复或漏行。
OFFSET 越大,数据库需要先扫描并丢弃前面的行,深分页性能会下降。生产环境可改用“基于游标的分页”(如
WHERE salary < 上一页末行薪资)替代大 OFFSET。可复用分页检查清单
- 排序键是否包含唯一列(如主键)作为最终决胜键?
- OFFSET 是否等于
(页码 - 1) × 每页行数? - 是否验证了最后一页不足一页、以及越界页返回 0 行的情况?
- 翻页时排序字段与方向是否保持一致?