MySQL

排序与分页

MySQL 用 ORDER BY 升序降序、多列排序与 NULL 处理,再用 LIMIT 做 TOP-N 与网页分页。

查出数据后,还要按顺序看、只看前几条、翻页看。今天练 ORDER BY 与 LIMIT——报表和列表页的标配。

系列:MySQL 入门到查询 · 第 12 / 17 篇(第 12 天 · 5 月 2 日)
上一篇:聚合与分组
下一篇:子查询
总目录:系列索引

环境说明

  • 库:train;表:emp(与第 11 天相同,请先 seed):
use train;

drop table if exists emp;
create table emp(
    empno int,
    ename varchar(20),
    sex varchar(4),
    job varchar(20),
    sal double,
    comm double,
    deptno int
);

insert into emp values
    (1001,'马云','男','经理',20000, 5000, 10),
    (1002,'王明','男','员工', 8000, null, 20),
    (1003,'李梅','女','员工', 7500,  800, 20),
    (1004,'张强','男','总监',30000, null, 30),
    (1005,'赵静','女','经理',18000, 2000, 30),
    (1006,'钱伟','男','员工', 9000, null, 30),
    (1007,'孙丽','女','员工', 7200,  300, 30),
    (1008,'周杰','男','总监',35000, null, 40);

练习数据以本篇 seed 为准。

1. ORDER BY:排序

order by 列名 [asc|desc]
关键字含义是否可省略
asc升序(小 → 大)默认,可省略
desc降序(大 → 小)不能省略,必须写
select * from emp order by sal asc;
select * from emp order by sal;
select * from emp order by sal desc;
写法效果
order by sal工资从低到高
order by sal desc工资从高到低

和 WHERE 一起用

select * from emp
where deptno in (20,30)
order by sal desc;

select * from emp
where sex='男' and job='员工'
order by sal desc;

概念顺序:

FROM → WHERE(筛行)→ SELECT → ORDER BY(排结果)→ LIMIT(截取)

排序里的 NULL

select * from emp order by comm;
select * from emp where comm is not null order by comm;

入门结论:排序时 NULL 常被当作最小值(升序时可能排在最前)。只想看有奖金的,先 where comm is not null。

排表达式 / 别名

select ename, sal, (sal + ifnull(comm,0)) * 12 as year_sal
from emp
order by year_sal desc;
写法说明
order by (sal+ifnull(comm,0))*12 desc可以对表达式排序
select ... as year_sal ... order by year_sal用别名更易读

ifnull(comm,0):若奖金为 NULL,当作 0,避免整列算式变 NULL(字符串/日期函数见第 9~10 天;此处先会用即可)。

2. 多列排序

select deptno, sal, ename
from emp
order by deptno asc, sal desc;

读法:

  1. 先按 deptno 升序
  2. 同一部门内,再按 sal 降序
order by 列1 规则, 列2 规则
要点说明
先排第一列决定大分组顺序
第二列在第一列相同的「组内」再排
select ename, deptno, sal from emp order by sal desc, ename;

先按工资降序;工资相同时再按姓名升序。

按日期排

若表有日期列(此处示意):

-- 越早的日期值越小 → order by hiredate 默认升序 = 从早到晚
-- select * from emp order by hiredate;
-- select * from emp order by hiredate desc;  -- 从晚到早

3. LIMIT:取前几行 / 分页

limit 行数
limit 偏移量, 行数
limit 行数 offset 偏移量

取前 N 行(TOP-N)

select * from emp limit 3;
select * from emp limit 0, 3;   -- 与上句等价(从第 0 行起取 3 行)

取中间某几行

-- 结果集重新编号:0,1,2,3,...
-- 跳过前 2 行,再取 3 行 → 大约是第 3~5 条(1 起数的习惯说法)
select * from emp limit 2, 3;
select * from emp limit 3 offset 2;
写法含义
limit 3最多 3 行
limit 2, 3从偏移 2 起,取 3 行
limit 3 offset 2同上,更易读

重要:LIMIT 的编号是结果集里的行号(从 0 开始),不是表里的 empno。

排序 + LIMIT:工资最高 / 最低

-- 最高工资的 1 人
select * from emp order by sal desc limit 1;

-- 最低工资的 1 人
select * from emp order by sal limit 1;

注意:若最低工资有多人并列,limit 1 只会显示其中一条。要「全部最低工资的人」,可用子查询(第 13 天)或窗口函数(进阶)。

select * from emp order by sal limit 0, 3;  -- 工资最低的前 3 条
select * from emp order by sal desc limit 3; -- 工资最高的前 3 条

和聚合、分组一起

select deptno, sum(sal) as s_sal
from emp
group by deptno
order by s_sal desc
limit 2;

部门工资总和最高的前 2 个部门。

执行顺序(简化):

WHERE → GROUP BY → HAVING → SELECT → ORDER BY → LIMIT

4. 网页分页怎么算(概念)

假设每页 10 条,第 Page 页(从 1 开始数):

起始行号 = PageSize * (Page - 1)
每页行数 = PageSize
页码起始语句示例
第 1 页0limit 0,10 或 limit 10
第 2 页10limit 10,10
第 3 页20limit 20,10
select * from emp order by empno limit 0, 3;
select * from emp order by empno limit 3, 3;
select * from emp order by empno limit 6, 3;

工作里通常:先 order by 排序稳定,再 limit 分页,否则翻页顺序可能乱跳。

5. WHERE / HAVING / LIMIT 怎么分工

子句作用典型例子
WHERE对原始行过滤deptno=30
HAVING对分组后再过滤(常用聚合条件)count(*)>=2
LIMIT对最终结果取前几行 / 分页limit 3
select deptno, count(*) as cnt
from emp
where deptno != 10
group by deptno
having count(*) >= 2
order by cnt desc
limit 2;

读法:先去掉 10 号部门 → 分组统计人数 → 只保留人数 ≥2 → 人数降序 → 取前 2 组。

6. 完整跟练脚本

use train;

drop table if exists emp;
create table emp(
    empno int, ename varchar(20), sex varchar(4), job varchar(20),
    sal double, comm double, deptno int
);
insert into emp values
    (1001,'马云','男','经理',20000, 5000, 10),
    (1002,'王明','男','员工', 8000, null, 20),
    (1003,'李梅','女','员工', 7500,  800, 20),
    (1004,'张强','男','总监',30000, null, 30),
    (1005,'赵静','女','经理',18000, 2000, 30),
    (1006,'钱伟','男','员工', 9000, null, 30),
    (1007,'孙丽','女','员工', 7200,  300, 30),
    (1008,'周杰','男','总监',35000, null, 40);

select * from emp order by sal;
select * from emp order by sal desc;
select * from emp where deptno in (20,30) order by sal desc;
select * from emp order by comm;
select * from emp where comm is not null order by comm;

select deptno, sal from emp order by deptno, sal desc;
select ename, sal, (sal+ifnull(comm,0))*12 as year_sal
from emp order by year_sal desc;

select * from emp limit 3;
select * from emp limit 2,3;
select * from emp order by sal desc limit 1;
select * from emp order by sal limit 3;

select deptno, sum(sal) as s_sal
from emp group by deptno
order by s_sal desc limit 2;

select * from emp order by empno limit 0,3;
select * from emp order by empno limit 3,3;

7. 速查表

目标写法
升序order by 列 或 order by 列 asc
降序order by 列 desc
多列排序order by 列1 desc, 列2 asc
用别名排select ... as x ... order by x
前 N 行limit N
偏移取行limit 偏移, 行数 / limit 行数 offset 偏移
TOP-Norder by ... desc limit N
分页起始PageSize * (Page - 1)

8. 今天的练习清单

  • order by sal 与 order by sal desc
  • where + order by
  • 多列:order by deptno, sal desc
  • limit 3、limit 2,3
  • 工资最高前 1、前 3
  • 部门工资总和降序取前 2
  • 模拟第 1/2 页,每页 3 条
  • 组合:where + group by + having + order by + limit

常见错误

易错:desc 漏写,结果成了升序。
易错:把 limit 的偏移当成 empno 业务编号。
易错:分页前不 order by,顺序不稳定。
易错:select 里未聚合的列与 group by 混用(第 11 天的行列平衡)。
易错:limit 1 以为能列出并列第一名(只会出一行)。
易错:order by 写了列名别名却忘了在 select 里定义。

小结

  1. ORDER BY:asc 默认可省,desc 必须写;多列时先第一列,再组内。
  2. 排序时 NULL 常当最小值;要排除先 is not null。
  3. LIMIT 用结果集行号(从 0 起):limit N / limit 偏移,N。
  4. TOP-N = 排序 + limit;网页分页 = 稳定 order by + 公式算偏移。
  5. WHERE → GROUP BY/HAVING → ORDER BY → LIMIT 概念顺序要记牢。
  6. 下一篇 子查询:一条 SQL 里再嵌一条查询。

系列导航:总目录 · 上一篇:聚合与分组 · 下一篇:子查询

相关阅读

全部文章 →
← 返回列表更多「MySQL」