跳到主要内容

条件查询、排序与分页

上一节的查询都是"整表端出来",但真实需求往往是:"查成绩大于 85 分的记录""按成绩从高到低排""每页只显示 3 条"。这就要用到本节的三大武器:WHERE(筛选)、ORDER BY(排序)、LIMIT(分页)。所有例子仍然基于 school 库的标准数据,请边看边跑、对照输出。

📖 本节导读

  • 掌握 WHERE 中的比较运算符(= != > < >= <=)和逻辑运算符(AND / OR / NOT
  • 掌握范围与集合条件:BETWEEN ANDIN,以及 LIKE 模糊查询(%_
  • 理解 NULL 的特殊性:必须用 IS NULL / IS NOT NULL,不能用 = NULL
  • 掌握 ORDER BY 单列/多列排序和 LIMIT 分页公式

先切换到 school 库:

USE school;

一、条件筛选:WHERE

WHERE 写在 FROM 表名 之后,只有满足条件的行才会出现在结果里,就像安检门——符合条件的放行,不符合的拦下。

1.1 比较运算符:= != > < >= <=

查询所有女生:

SELECT * FROM student WHERE gender = '女';
+----+--------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+--------+--------+------------+----------+
| 3 | 王小红 | 女 | 2006-01-20 | 2 |
| 4 | 赵敏 | 女 | 2005-11-08 | 2 |
+----+--------+--------+------------+----------+
2 rows in set (0.00 sec)

查询成绩大于 85 分的记录:

SELECT * FROM score WHERE score > 85;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 1 | 1 | 1 | 90.0 |
| 3 | 1 | 3 | 88.0 |
| 5 | 2 | 2 | 92.0 |
| 6 | 3 | 1 | 95.0 |
+----+------------+-----------+-------+
4 rows in set (0.00 sec)

查询不在一班的学生(!= 表示不等于,也可写作 <>):

SELECT name, class_id FROM student WHERE class_id != 1;
+--------+----------+
| name | class_id |
+--------+----------+
| 王小红 | 2 |
| 赵敏 | 2 |
+--------+----------+
2 rows in set (0.00 sec)

日期也能比较(按时间先后),查 2006 年及以后出生的学生:

SELECT name, birthday FROM student WHERE birthday >= '2006-01-01';
+--------+------------+
| name | birthday |
+--------+------------+
| 王小红 | 2006-01-20 |
| 陈刚 | 2006-05-30 |
+--------+------------+
2 rows in set (0.00 sec)

1.2 逻辑运算符:AND / OR / NOT

多个条件可以组合:AND(并且,都满足)、OR(或者,满足其一即可)、NOT(取反)。

一班的男生(两个条件都要满足):

SELECT * FROM student WHERE gender = '男' AND class_id = 1;
+----+------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+------+--------+------------+----------+
| 1 | 张三 | 男 | 2005-03-15 | 1 |
| 2 | 李四 | 男 | 2005-07-01 | 1 |
| 5 | 陈刚 | 男 | 2006-05-30 | 1 |
+----+------+--------+------------+----------+
3 rows in set (0.00 sec)

成绩特别好(>90)或不及格(<60)的记录(满足其一即可):

SELECT * FROM score WHERE score > 90 OR score < 60;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 5 | 2 | 2 | 92.0 |
| 6 | 3 | 1 | 95.0 |
| 9 | 4 | 1 | 58.0 |
+----+------------+-----------+-------+
3 rows in set (0.00 sec)

NOT 取反,不在一班的学生(效果同 class_id != 1):

SELECT name, class_id FROM student WHERE NOT class_id = 1;

⚠️ AND 的优先级比 OR 高,混用时一定加括号表达清楚意图。例如"一班的女生或二班的女生"应写成 WHERE (class_id = 1 OR class_id = 2) AND gender = '女',不加括号含义会完全不同。

1.3 范围条件:BETWEEN AND

BETWEEN a AND b 表示"在 a 到 b 之间(含两端)",等价于 >= a AND <= b

SELECT * FROM score WHERE score BETWEEN 80 AND 90;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 1 | 1 | 1 | 90.0 |
| 2 | 1 | 2 | 85.0 |
| 3 | 1 | 3 | 88.0 |
| 10 | 4 | 3 | 81.0 |
+----+------------+-----------+-------+
4 rows in set (0.00 sec)

注意 90.0 也被查出来了——两端是包含的。

1.4 集合条件:IN

IN (值1, 值2, ...) 表示"等于列表中的任意一个",比一串 OR 简洁得多:

SELECT * FROM student WHERE id IN (1, 3, 5);
+----+--------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+--------+--------+------------+----------+
| 1 | 张三 | 男 | 2005-03-15 | 1 |
| 3 | 王小红 | 女 | 2006-01-20 | 2 |
| 5 | 陈刚 | 男 | 2006-05-30 | 1 |
+----+--------+--------+------------+----------+
3 rows in set (0.00 sec)

取反用 NOT IN (1, 3, 5),会查出李四和赵敏。

1.5 模糊查询:LIKE

精确匹配用 =,"长得像"就用 LIKE,配合两个通配符:

  • %:匹配任意个字符(0 个也行)
  • _:匹配恰好 1 个字符

查所有姓张的学生(张开头,后面随便):

SELECT * FROM student WHERE name LIKE '张%';
+----+------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+------+--------+------------+----------+
| 1 | 张三 | 男 | 2005-03-15 | 1 |
+----+------+--------+------------+----------+
1 row in set (0.00 sec)

查名字中包含"红"字的学生(前后都可以有任意字符):

SELECT * FROM student WHERE name LIKE '%红%';
+----+--------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+--------+--------+------------+----------+
| 3 | 王小红 | 女 | 2006-01-20 | 2 |
+----+--------+--------+------------+----------+
1 row in set (0.00 sec)

查姓张且名字正好两个字的学生(_ 恰好占一个字符的位置):

SELECT * FROM student WHERE name LIKE '张_';

只有"张三"匹配(如果有个"张小明",他匹配 '张%' 但不匹配 '张_')。

1.6 空值判断:IS NULL / IS NOT NULL

还记得吗?陈刚(id=5)的计算机成绩是 NULL。想找出"还没出成绩"的记录,新手最容易这样写:

SELECT * FROM score WHERE score = NULL; -- ❌ 错误示范
Empty set (0.00 sec)

查不到任何东西! 因为 NULL 表示"未知",任何值和 NULL 用 = 比较,结果既不是真也不是假,而是 NULL(未知),所以一行都不会被选中。类比:问"这个未知数等不等于另一个未知数?"答案只能是"不知道"。

判断空值必须用专门的 IS NULL

SELECT * FROM score WHERE score IS NULL;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 12 | 5 | 3 | NULL |
+----+------------+-----------+-------+
1 row in set (0.00 sec)

反过来,查"已出成绩"的记录用 IS NOT NULL

SELECT * FROM score WHERE score IS NOT NULL;

会查出其余 11 行(11 rows in set)。

二、排序:ORDER BY

ORDER BY 写在 WHERE 之后,控制结果的顺序:

  • ASC:升序(从小到大),默认值,可省略
  • DESC:降序(从大到小)

2.1 单列排序

按成绩从高到低:

SELECT * FROM score ORDER BY score DESC;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 6 | 3 | 1 | 95.0 |
| 5 | 2 | 2 | 92.0 |
| 1 | 1 | 1 | 90.0 |
| 3 | 1 | 3 | 88.0 |
| 2 | 1 | 2 | 85.0 |
| 10 | 4 | 3 | 81.0 |
| 4 | 2 | 1 | 76.0 |
| 11 | 5 | 2 | 73.0 |
| 8 | 3 | 3 | 70.0 |
| 7 | 3 | 2 | 60.0 |
| 9 | 4 | 1 | 58.0 |
| 12 | 5 | 3 | NULL |
+----+------------+-----------+-------+
12 rows in set (0.00 sec)

注意:MySQL 把 NULL 当作最小值处理——升序时 NULL 排最前,降序时排最后(如上)。

按生日从早到晚(升序可省略 ASC):

SELECT name, birthday FROM student ORDER BY birthday;
+--------+------------+
| name | birthday |
+--------+------------+
| 张三 | 2005-03-15 |
| 李四 | 2005-07-01 |
| 赵敏 | 2005-11-08 |
| 王小红 | 2006-01-20 |
| 陈刚 | 2006-05-30 |
+--------+------------+
5 rows in set (0.00 sec)

2.2 多列排序

多个排序条件用逗号隔开:先按第一列排,第一列相同的再按第二列排。例如先按班级升序,同班的再按生日降序(年龄小的在前):

SELECT name, class_id, birthday FROM student
ORDER BY class_id ASC, birthday DESC;
+--------+----------+------------+
| name | class_id | birthday |
+--------+----------+------------+
| 陈刚 | 1 | 2006-05-30 |
| 李四 | 1 | 2005-07-01 |
| 张三 | 1 | 2005-03-15 |
| 王小红 | 2 | 2006-01-20 |
| 赵敏 | 2 | 2005-11-08 |
+--------+----------+------------+
5 rows in set (0.00 sec)

每一列可以单独指定 ASC/DESC,互不影响。

三、分页:LIMIT

结果太多时一页页看,就是分页LIMIT 永远写在整条语句的最后。

3.1 LIMIT n:只取前 n 条

成绩排行榜前三名:

SELECT * FROM score ORDER BY score DESC LIMIT 3;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 6 | 3 | 1 | 95.0 |
| 5 | 2 | 2 | 92.0 |
| 1 | 1 | 1 | 90.0 |
+----+------------+-----------+-------+
3 rows in set (0.00 sec)

3.2 LIMIT offset, n:跳过 offset 条,再取 n 条

offset(偏移量)表示"跳过前多少条"。比如每页 2 条,看学生表的第 2 页(跳过前 2 条,取 2 条):

SELECT * FROM student LIMIT 2, 2;
+----+--------+--------+------------+----------+
| id | name | gender | birthday | class_id |
+----+--------+--------+------------+----------+
| 3 | 王小红 | 女 | 2006-01-20 | 2 |
| 4 | 赵敏 | 女 | 2005-11-08 | 2 |
+----+--------+--------+------------+----------+
2 rows in set (0.00 sec)

正好是第 3、第 4 名学生——跳过了前 2 条,取了 2 条。

3.3 分页公式

每页 pageSize 条、要看第 pageNo 页时:

LIMIT (pageNo - 1) * pageSize, pageSize

以每页 2 条为例:第 1 页 LIMIT 0, 2,第 2 页 LIMIT 2, 2,第 3 页 LIMIT 4, 2(学生表第 3 页只剩陈刚 1 条)。

3.4 各子句的书写顺序

把本节内容串起来,一条完整查询的顺序固定为:

SELECTFROMWHERE 条件 ORDER BY 排序列 LIMIT 偏移, 条数;

例如:已出成绩中的倒数前两名:

SELECT * FROM score WHERE score IS NOT NULL ORDER BY score ASC LIMIT 2;
+----+------------+-----------+-------+
| id | student_id | course_id | score |
+----+------------+-----------+-------+
| 9 | 4 | 1 | 58.0 |
| 7 | 3 | 2 | 60.0 |
+----+------------+-----------+-------+
2 rows in set (0.00 sec)

⚠️ 新手常见坑

  1. = NULL 判断空值:永远返回空结果。必须用 IS NULL / IS NOT NULL
  2. AND / OR 混用不加括号:AND 优先级高于 OR,语义可能与直觉相反。混用必加括号。
  3. %_ 记混% 任意个字符,_ 恰好一个字符。'张%' 能匹配"张三丰",'张_' 不能。
  4. 子句顺序写错LIMIT 写在 ORDER BY 前面会报语法错误。牢记 WHERE → ORDER BY → LIMIT。
  5. 以为 LIMIT 2, 2 是"第 2 到第 2 条":第一个数字是跳过的条数(偏移量),不是起始页码或起始行号。
  6. LIKE 以 % 开头的查询在大表上很慢:如 '%红%' 无法利用索引,数据量大时慎用(索引章节会详细解释)。

📝 小结

  • WHERE 筛选行:比较运算符 = != > < >= <=;逻辑组合 AND / OR / NOT(混用加括号)。
  • 范围 BETWEEN a AND b(含两端);集合 IN (...);模糊 LIKE% 任意个字符、_ 单个字符)。
  • NULL 是"未知",比较必须用 IS NULL / IS NOT NULL= NULL 永远查不到。
  • ORDER BY 列 [ASC|DESC] 排序,可多列逗号分隔逐级排序;NULL 被视为最小。
  • LIMIT n 取前 n 条;LIMIT offset, n 跳过 offset 条取 n 条;第 pageNo 页 = LIMIT (pageNo-1)*pageSize, pageSize
  • 固定顺序:SELECT ... FROM ... WHERE ... ORDER BY ... LIMIT ...

✍️ 练习题

1. 查询二班(class_id = 2)的所有学生。

参考答案
SELECT * FROM student WHERE class_id = 2;

结果为王小红、赵敏两行。

2. 查询成绩在 70 到 90 之间(含两端)的成绩记录,分别用 BETWEEN 和不用 BETWEEN 两种写法。

参考答案
SELECT * FROM score WHERE score BETWEEN 70 AND 90;
SELECT * FROM score WHERE score >= 70 AND score <= 90;

结果为 id 1(90.0)、2(85.0)、3(88.0)、4(76.0)、8(70.0)、10(81.0)、11(73.0),共 7 行。

3. 查询名字里含"小"字的学生,以及名字恰好是两个字的学生,各写一条语句。

参考答案
SELECT * FROM student WHERE name LIKE '%小%'; -- 王小红
SELECT * FROM student WHERE name LIKE '__'; -- 张三、李四、赵敏、陈刚(两个下划线)

4. 查询"还没出成绩"的记录是哪个学生的哪门课(先只查 score 表找出该行)。为什么不能写 WHERE score = NULL

参考答案
SELECT * FROM score WHERE score IS NULL;

查出 id=12、student_id=5、course_id=3,即陈刚的计算机课。不能用 = NULL:NULL 表示未知,任何值与 NULL 用 = 比较的结果仍是 NULL(不为真),因此一行也匹配不上。

5. 已出成绩按分数从高到低排序,每页 4 条,写出查询"第 2 页"的语句并给出结果第一行是哪条记录。

参考答案
SELECT * FROM score WHERE score IS NOT NULL
ORDER BY score DESC LIMIT 4, 4;

偏移量 = (2-1)×4 = 4。降序排列为 95、92、90、88、85、81、76、73、70、60、58,第 2 页从第 5 条开始,第一行是 85.0(id=2,张三的英语成绩)。


⬅ 上一节 | 🏠 返回目录 | 下一节 ➡