条件查询、排序与分页
上一节的查询都是"整表端出来",但真实需求往往是:"查成绩大于 85 分的记录""按成绩从高到低排""每页只显示 3 条"。这就要用到本节的三大武器:
WHERE(筛选)、ORDER BY(排序)、LIMIT(分页)。所有例子仍然基于 school 库的标准数据,请边看边跑、对照输出。
📖 本节导读
- 掌握 WHERE 中的比较运算符(
= != > < >= <=)和逻辑运算符(AND / OR / NOT) - 掌握范围与集合条件:
BETWEEN AND、IN,以及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 各子句的书写顺序
把本节内容串起来,一条完整查询的顺序固定为:
SELECT 列 FROM 表 WHERE 条件 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)
⚠️ 新手常见坑
- 用
= NULL判断空值:永远返回空结果。必须用IS NULL/IS NOT NULL。 - AND / OR 混用不加括号:AND 优先级高于 OR,语义可能与直觉相反。混用必加括号。
%和_记混:%任意个字符,_恰好一个字符。'张%' 能匹配"张三丰",'张_' 不能。- 子句顺序写错:
LIMIT写在ORDER BY前面会报语法错误。牢记 WHERE → ORDER BY → LIMIT。 - 以为
LIMIT 2, 2是"第 2 到第 2 条":第一个数字是跳过的条数(偏移量),不是起始页码或起始行号。 - 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,张三的英语成绩)。