数据库系统概论(八)SQL单表查询语言超详细讲解(附带例题表格对比带你一步步掌握)
数据库系统概论(八)SQL单表查询语言超详细讲解(附带例题表格对比带你一步步掌握)
- 前言
- 一、创建表(了解一下就好,后面会详细讲)
- 二、数据查询的概念
- 三、单表查询语言的种类
前言
- 之前我们已经简单认识了 SQL 语言(结构化查询语言),并了解了它的基本概念(比如表、元组、属性、数据库等)。
- 现在,我们正式进入 SQL 核心功能 的学习 —— SQL 查询语言,这是日常使用最频繁、也是掌握 SQL 的关键部分。
我的个人主页,欢迎来阅读我的其他文章
https://blog.ZEEKLOG.net/2402_83322742?spm=1011.2415.3001.5343
我的数据库系统概论专栏
https://blog.ZEEKLOG.net/2402_83322742/category_12911520.html?spm=1001.2014.3001.5482
一、创建表(了解一下就好,后面会详细讲)
- 在开始之前我们需要创建这三张表,以方便我们后续的查询

CREATETABLE Student ( Sno CHAR(7)PRIMARYKEY,-- 学号,主键,假设学号固定 7 位 Sname VARCHAR(20)NOTNULL,-- 姓名,非空 Ssex CHAR(2)CHECK(Ssex IN('男','女')),-- 性别,限制为男或女 Sbirthdate DATE,-- 出生日期,日期类型 Smajor VARCHAR(50)-- 主修专业);INSERTINTO Student (Sno, Sname, Ssex, Sbirthdate, Smajor)VALUES('20180001','李勇','男','2000-03-08','信息安全'),('20180002','刘晨','女','1999-09-01','计算机科学与技术'),('20180003','王敏','女','2001-08-01','计算机科学与技术'),('20180004','张立','男','2000-01-08','计算机科学与技术'),('20180005','陈新奇','男','2001-11-01','信息管理与信息系统'),('20180006','赵明','男','2000-06-12','数据科学与大数据技术'),('20180007','王佳佳','女','2001-12-07','数据科学与大数据技术');
CREATETABLE Course ( Cno CHAR(5)PRIMARYKEY,-- 课程号,主键,假设课程号固定 5 位 Cname VARCHAR(50)NOTNULL,-- 课程名,非空 Ccredit INT,-- 学分,整数类型 Cpno CHAR(5),-- 先修课课程号FOREIGNKEY(Cpno)REFERENCES Course(Cno)-- 外键,先修课引用自身课程号);INSERTINTO Course (Cno, Cname, Ccredit, Cpno)VALUES('81001','程序设计基础与C语言',4,NULL),('81002','数据结构',4,'81001'),('81003','数据库系统概论',4,'81002'),('81004','信息系统概论',4,'81003'),('81005','操作系统',4,'81001'),('81006','Python语言',3,'81002'),('81007','离散数学',4,NULL),('81008','大数据技术概论',4,'81003');
CREATETABLE SC ( Sno CHAR(7),-- 学号 Cno CHAR(5),-- 课程号 Grade INT,-- 成绩,整数类型 Semester CHAR(4),-- 选课学期,如 "20192" Teachingclass VARCHAR(20),-- 教学班PRIMARYKEY(Sno, Cno),-- 联合主键(学号 + 课程号)FOREIGNKEY(Sno)REFERENCES Student(Sno),-- 外键,关联学生表FOREIGNKEY(Cno)REFERENCES Course(Cno)-- 外键,关联课程表);INSERTINTO SC (Sno, Cno, Grade, Semester, Teachingclass)VALUES('20180001','81001',85,'20192','81001-01'),('20180001','81002',96,'20201','81002-01'),('20180001','81003',87,'20202','81003-01'),('20180002','81001',80,'20192','81001-02'),('20180002','81002',98,'20201','81002-01'),('20180002','81003',71,'20202','81003-02'),('20180003','81001',81,'20192','81001-01'),('20180003','81002',76,'20201','81002-02'),('20180004','81001',56,'20192','81001-02'),('20180004','81003',97,'20201','81002-02'),('20180005','81003',68,'20202','81003-01');二、数据查询的概念
2.1 什么是数据查询?
你可以把 数据库 想象成一个超级大的电子表格(比如Excel),里面存满了各种数据(比如学生信息、订单记录、商品列表等)。
数据查询 就是:从这个“超级表格”里快速找到你需要的信息。
比如:
- 你想知道“数学成绩超过80分的学生有哪些?”
- 或者“昨天销量超过100件的商品有哪些?”
这些“找信息”的过程,就是数据查询。
2.2 数据查询的语句样式:
大部分数据库(比如MySQL、SQL Server、Oracle)都用一种叫 SQL(结构化查询语言) 的语句来查询数据。
它的语法很像“人话”,比如:
1. 最基本的查询语句(查“某张表的某几列数据”)
SELECT 列1, 列2, 列3-- 你想查的“表格中的列”(比如“姓名”“分数”)FROM 表名;-- 数据所在的“表格名称”(比如“学生表”)例子:查“学生表”中的所有学生的姓名和数学成绩:
SELECT 姓名, 数学成绩 FROM 学生表;2. 带条件的查询(加一个“筛选条件”,只查符合条件的数据)
如果想只查“数学成绩大于80分”的学生,就加一个 WHERE 条件:
SELECT 列1, 列2-- 想查的列FROM 表名 -- 表格名WHERE 条件;-- 筛选条件(比如“数学成绩 > 80”)例子:
SELECT 姓名, 数学成绩 FROM 学生表 WHERE 数学成绩 >80;-- 只查数学成绩超过80分的学生3. 其他常用“小工具”
去重:如果想查“不重复”的数据,比如“有哪些不同的班级”,用 DISTINCT:
SELECTDISTINCT 班级 -- 只显示不同的班级名称,重复的只留一个FROM 学生表;查所有列:如果想查表格里的所有列,不用一个个列名写出来,直接用 *:
SELECT*-- * 代表“所有列”FROM 学生表;三、单表查询语言的种类
接着我们用上面刚刚所创建的表



3.1 单表查询
就是只从 一个表 里查数据,比如只查学生表(Student)、课程表(Course)或选课表(SC)中的数据。
3.1.1 查询表中的若干列
1. 查询指定的列(只选需要的信息)
- 作用:只查表中某几列的数据,比如只查学生的学号和姓名,不查其他信息(性别、年龄等)。
- 例子:
查姓名、学号、专业(列的顺序可以自己调):
SELECT Sname, Sno, Smajor -- 顺序和表中不一定一样,按需求排FROM Student;查全体学生的学号和姓名:
SELECT Sno, Sname -- 只查这两列FROM Student;-- 从学生表查语法:
SELECT 列1, 列2, 列3-- 想查的列,用逗号隔开FROM 表名;-- 从哪个表查2. 查询全部列(查所有信息)
例子:查学生的所有详细信息:
SELECT*FROM Student;-- 简单又快捷!方法2(推荐):用 * 代替所有列,一键查询所有信息:
SELECT*-- * 表示“所有列”FROM 表名;方法1:列出表的所有列(麻烦,表列多的时候不推荐):
SELECT 列1, 列2, 列3,..., 列N -- 把所有列名写出来FROM 表名;3. 查询计算后的值(加工数据)
- 作用:表中没有直接的数据,但可以通过 公式、函数 计算出来。
比如表中存的是出生日期(Sbirthdate),想算年龄,就用当前日期减出生日期。TIMESTAMPDIFF(YEAR, 出生日期, 当前日期):算两个日期相差多少年(年龄)。CURDATE():获取当前日期(2025-05-12,例子中的今天)。
例子:查学生的学号、姓名和年龄(年龄用函数计算):
SELECT Sno, Sname, TIMESTAMPDIFF(YEAR, Sbirthdate, CURDATE())AS Age -- 计算年龄,别名是AgeFROM Student;语法:
SELECT 列1, 计算表达式 [AS 别名]-- AS 给结果起个名字(可选)FROM 表名;4. 去重查询
- 作用:如果某列有重复数据(比如选课表SC中同个学生选多门课,学号会重复),想只看唯一的学号,就用
DISTINCT。
例子:查所有选过课的学生学号(不重复):
SELECTDISTINCT Sno -- 不管一个学生选了多少课,学号只出现一次FROM SC;语法:
SELECTDISTINCT 列名 -- 只保留该列的唯一值FROM 表名;总结
| 场景 | 语法示例 | 说明 |
|---|---|---|
| 指定列 | SELECT 列1, 列2 FROM 表名; | 只查需要的列,列名用逗号隔开。 |
| 所有列 | SELECT * FROM 表名; | * 代表所有列,简单方便。 |
| 计算值 | SELECT 列, 表达式 AS 别名 FROM 表名; | 用公式/函数加工数据,AS 起别名(可选)。 |
| 去重 | SELECT DISTINCT 列 FROM 表名; | 去掉重复的行,只保留唯一值。 |
3.1.2 选择表中的若干元组
1.基础:查询条件放在哪里?
所有筛选条件都写在 WHERE 子句里(如果是分组后的筛选,用 HAVING,这里先重点学 WHERE)。
格式:
SELECT 列名 FROM 表名 WHERE 条件;2.常用查询条件分类

3. 简单比较(比大小、是否相等)
- 谓词:
=、>、<、>=、<=、!=(或<>),以及NOT+这些符号(取反)。 - 作用:直接对比数据是否符合某个值或范围。
- 例子:
查2001年及以后出生的学生(用 year(日期列) 提取年份):
SELECT...WHEREyear(Sbirthdate)>=2001;查成绩不及格(<60分)的学生学号(去重,避免重复):
SELECTDISTINCT Sno FROM SC WHERE Grade <60;查“计算机科学与技术”专业的学生姓名:
SELECT Sname FROM Student WHERE Smajor ='计算机科学与技术';4. 范围查询(某个区间内/外)
- 谓词:
BETWEEN 最小值 AND 最大值:包含两端,比如20-23岁之间。NOT BETWEEN 最小值 AND 最大值:不在这个区间内。
- 作用:快速筛选连续区间的数据,比用
>= 最小值 AND <= 最大值更简洁。 - 例子:
查年龄不在20-23岁之间的学生,只需加 NOT:
...WHERE...NOTBETWEEN20AND23;查年龄20-23岁的学生(用 TIMESTAMPDIFF(year, 出生日期, 当前日期) 计算年龄):
SELECT...WHERE TIMESTAMPDIFF(year, Sbirthdate, CURDATE())BETWEEN20AND23;5. 集合查询(是否在指定列表中)
- 谓词:
IN (值1, 值2, ...):数据等于列表中的任意一个。NOT IN (值1, 值2, ...):数据不在列表中。
- 作用:快速筛选多个指定值,比用多个
OR更方便。 - 例子:
查不在这两个专业的学生,加 NOT:
...WHERE Smajor NOTIN(...);等价于用 OR 连接:
...WHERE Smajor ='计算机科学与技术'OR Smajor ='信息安全';查“计算机科学与技术”或“信息安全”专业的学生:
SELECT...WHERE Smajor IN('计算机科学与技术','信息安全');6. 字符匹配(模糊查询,含通配符)
- 谓词:
LIKE '匹配串':数据符合匹配规则(支持通配符)。NOT LIKE '匹配串':不符合匹配规则。
- 通配符:
%:代表任意长度的字符串(包括0个字符,即空)。_:代表任意单个字符。
- 例子:
转义特殊字符(如果数据中包含 % 或 _,需要用 ESCAPE 声明转义符,比如 \):
-- 查名称为“DB_Design”的课程(下划线是普通字符,不是通配符)WHERE Cname LIKE'DB\_Design'ESCAPE '\';-- 用\转义下划线排除匹配(不姓刘的学生):
WHERE Sname NOTLIKE'刘%';指定长度和位置(比如学号以2023开头,课程号第5位是6,总长度5位):
WHERE Sno LIKE'2023%';-- 学号以2023开头(后面任意长度)WHERE Cno LIKE'81__6';-- 课程号第1-2位是81,第3-4位任意单个字符,第5位是6(共5位)以某个字符开头(比如“刘”姓):
WHERE Sname LIKE'刘%';-- 姓名以“刘”开头,后面任意字符固定字符串(精确匹配,等价于 =):
WHERE Sno LIKE'20180003';-- 等价于 Sno = '20180003'5. 空值判断(数据是否为空)
- 谓词:
IS NULL:数据为空(比如没参加考试,成绩为空)。IS NOT NULL:数据不为空。
- 注意:不能用
=或!=判断空值,必须用IS! - 例子:
查有成绩的学生:
...WHERE Grade ISNOTNULL;查缺少成绩的学生(成绩列为空):
SELECT Sno, Cno FROM SC WHERE Grade ISNULL;6. 多重条件组合(多个条件一起用)
- 逻辑运算符:
AND:多个条件同时满足(“且”)。OR:至少满足一个条件(“或”)。NOT:对条件取反(“非”)。
- 优先级:
NOT > AND > OR,但建议用括号明确顺序,避免出错。 - 例子:
查“计算机科学与技术”专业 或 成绩不及格的学生(用括号区分优先级):
SELECT...WHERE(Smajor ='计算机科学与技术')OR(Grade <60);查“计算机科学与技术”专业,且2004年及以后出生的学生:
SELECT...WHERE Smajor ='计算机科学与技术'ANDyear(Sbirthdate)>=2004;总结:
- 精确比较(等于、大于等):用
=、>、<等符号。 - 区间范围(20-23岁):用
BETWEEN AND或NOT BETWEEN AND。 - 多个指定值(两个专业):用
IN或NOT IN。 - 模糊匹配(姓刘、学号开头):用
LIKE,搭配%(任意长度)、_(单个字符),特殊字符用ESCAPE转义。 - 空值处理(有无成绩):用
IS NULL或IS NOT NULL。 - 多个条件组合:用
AND(同时满足)、OR(满足其一),必要时加括号。
3.1.3 ORDER BY子句
1. ORDER BY子句的作用
一句话:让查询结果按照你指定的列(或多列)排序,就像给数据“整理队形”!
2. 基本语法
SELECT 列1, 列2FROM 表名 WHERE 条件 ORDERBY 排序列 [ASC/DESC];ORDER BY必须放在WHERE子句之后(如果有WHERE的话)。ASC(Ascending):升序(从小到大,默认)。DESC(Descending):降序(从大到小)。
3.单字段排序:按一个列排序
例子1:查询选修了81003号课程的学生学号和成绩,按成绩降序排列(高分在前)。
SELECT Sno, Grade FROM SC WHERE Cno='81003'ORDERBY Grade DESC;- 结果:成绩最高的学生排在最前面,最低的在最后。
例子2:查询所有学生的姓名,按姓名升序排列(默认升序,可省略ASC)。
SELECT Sname FROM Student ORDERBY Sname;-- 等价于 ORDER BY Sname ASC- 结果:姓名按字母或拼音顺序排列(A-Z或拼音首字母)。
4.多字段排序:按多个列排序
规则:先按第一列排序,如果第一列值相同,则按第二列排序,以此类推。
例子:查询全体学生的选课情况,先按课程号升序,同一课程内按成绩降序。
SELECT*FROM SC ORDERBY Cno, Grade DESC;- 结果:
- 课程号(Cno)从小到大排列。
- 若课程号相同(比如都是81003),则按成绩(Grade)从高到低排列。
5.注意事项
- 空值(NULL)的排序:
- 不同数据库系统处理不同,常见两种:
- NULL值排在最前(如MySQL)。
- NULL值排在最后(如SQL Server)。
- 若想控制NULL的位置,可结合
IS NULL或COALESCE函数处理。
- 不同数据库系统处理不同,常见两种:
- 排序性能:
- 对大量数据排序时,会消耗更多资源。
- 尽量在排序字段上创建索引,提高速度。
6.总结:
- 常见场景:
- 成绩排名(降序)。
- 时间线(最新的在前,降序)。
- 字典序(姓名、商品名升序)。
多字段排序:
ORDERBY 列1[ASC/DESC], 列2[ASC/DESC];-- 先按列1,再按列2单字段排序:
ORDERBY 列名 ASC;-- 升序(默认)ORDERBY 列名 DESC;-- 降序3.1.4 聚集函数
1.什么是聚集函数?
一句话总结:聚集函数专门用来 “批量计算” 数据,比如统计有多少行、求和、求平均等。
举个生活例子:
- 想知道全班有多少人?用
COUNT。 - 想算全班数学平均分?用
AVG。 - 想找最高分、最低分?用
MAX和MIN。
2、5 个常用聚集函数

3. 统计个数:COUNT
- 功能:数“有多少个”。
- 两种用法:
COUNT(DISTINCT 列名):先去重,再统计非空值的数量。
▶ 例:查选修了课程的学生人数(同一个学生选多门课,只算一次)
SELECTCOUNT(DISTINCT Sno)FROM SC;-- 不管选了多少门课,每个学生只算1次COUNT(列名):统计这一列中 非空值的数量(排除 NULL)。
▶ 例:查选修了课程且有成绩的学生人数(Grade 列非空)
SELECTCOUNT(Grade)FROM SC;-- 只数有成绩的行,忽略成绩为空的COUNT(*):统计表格中 所有行的数量(包括有 NULL 的行)。
▶ 例:查学生总人数
SELECTCOUNT(*)FROM Student;-- 不管学生信息是否完整,只要有记录就数进去4. 求和:SUM(只能用于数值列)
- 功能:把某一列的数值加起来。
用法:SUM(列名),可选 DISTINCT 去重后求和(很少用,默认 ALL 算所有值)。
▶ 例:计算学号 20180003 的学生选修课程的总学分(需要连接 SC 和 Course 表,因为学分在 Course 表中)
SELECTSUM(Credit)FROM SC, Course WHERE Sno='20180003'AND SC.Cno=Course.Cno;-- 通过课程号连接,把选的课的学分加起来5. 求平均值:AVG(只能用于数值列)
功能:计算某一列数值的平均值(自动忽略 NULL)。
▶ 例:算选修 81001 号课程的学生平均成绩
SELECTAVG(Grade)FROM SC WHERE Cno='81001';6. 求最值:MAX和 MIN
MAX(列名):找某一列的最大值(数值、日期、字符串都能用,字符串按字典序)。MIN(列名):找某一列的最小值(同上)。
▶ 例:查 81001 号课程的最高分和最低分
功能:
SELECTMAX(Grade)AS 最高分,MIN(Grade)AS 最低分 FROM SC WHERE Cno='81001';7.关键细节
NULL值的处理:COUNT(*):不管有没有NULL,只要有行就统计。COUNT(列名)、SUM、AVG、MAX、MIN:自动忽略NULL(当列值是NULL时,不参与计算)。
DISTINCT 和 ALL:- 默认为
ALL(计算所有值,包括重复的)。 DISTINCT表示去重后再计算,比如COUNT(DISTINCT Sno)只算不同的学生。
- 默认为
- 多表使用:
- 如果要统计的列在多个表中,需要先通过 表连接(比如
SC和Course用课程号连接),再用聚集函数。
- 如果要统计的列在多个表中,需要先通过 表连接(比如
8.经典示例拆解(从简单到复杂)
| 需求 | 代码示例 | 核心逻辑 |
|---|---|---|
| 学生总人数 | SELECT COUNT(*) FROM Student; | 数所有学生记录,不管信息是否完整。 |
| 有成绩的学生选课记录数 | SELECT COUNT(Grade) FROM SC; | 只数 Grade 列不为空的行(有成绩的选课记录)。 |
| 选修过课程的学生人数(去重) | SELECT COUNT(DISTINCT Sno) FROM SC; | 同一个学生选多门课,只算1次(去重)。 |
| 某课程平均成绩 | SELECT AVG(Grade) FROM SC WHERE Cno='81001'; | 先筛选课程,再算平均成绩(自动忽略成绩为空的行)。 |
| 跨表求总学分 | SELECT SUM(Credit) FROM SC, Course ... | 通过课程号连接两张表,把学生选的所有课的学分加起来。 |
9.练习
- 条件:
Smajor='软件工程'且Ssex='女',统计人数用COUNT(*)。 - 条件:
Semester='2023-2'(假设学期格式如此)、Cno='81003'、Grade<60,统计人数用COUNT(*)。 - 直接对
Course表的Credit列求和。 - 用
MAX、MIN、AVG三个函数一起统计。
81002号课程的最高分、最低分、平均分:
SELECTMAX(Grade)AS 最高分,MIN(Grade)AS 最低分,AVG(Grade)AS 平均分 FROM SC WHERE Cno='81002';全部课程的总学分:
SELECTSUM(Credit)FROM Course;2023年第2学期“81003”课程的不及格人数:
SELECTCOUNT(*)FROM SC WHERE Semester='2023-2'AND Cno='81003'AND Grade<60;查询软件工程专业的女生总数:
SELECTCOUNT(*)FROM Student WHERE Smajor='软件工程'AND Ssex='女';10.总结:
- 数数量:
- 所有行:
COUNT(*)。 - 某列非空值:
COUNT(列名)。 - 去重后数量:
COUNT(DISTINCT 列名)。
- 所有行:
- 算总和/平均(仅限数值列):
SUM(列名)、AVG(列名)。 - 找最值:
MAX(列名)(最大)、MIN(列名)(最小)。
3.1.5 GROUP BY子句
1.什么是GROUP BY子句?
一句话总结:把数据按某一列或多列「分组」,然后对每个组分别进行统计(比如算每组的平均分、人数等)。
举个生活例子:
- 想按「班级」统计每个班的平均分?用
GROUP BY 班级。 - 想按「课程号」统计每门课的及格人数?用
GROUP BY 课程号。
2.基本语法与核心逻辑
SELECT 分组列, 聚集函数(其他列)-- 分组列必须出现在GROUP BY中FROM 表名 [WHERE 行级条件]-- 先筛选行,再分组GROUPBY 分组列 [, 分组列2,...]-- 按一列或多列分组[HAVING 组级条件]-- 对分组后的结果筛选(常用聚集函数)[ORDERBY 排序列];-- 最后排序3.关键逻辑步骤:
- 先过滤行:用
WHERE筛选符合条件的行(还没分组时就过滤)。 - 再分组:按
GROUP BY的列把数据分成多个组(同组的分组列值相同)。 - 组内统计:对每个组用聚集函数(如
AVG(Grade)、COUNT(*))计算。 - 筛选组:用
HAVING筛选掉不符合条件的组(比如平均成绩<60分的组)。 - 最后排序:用
ORDER BY给结果排序。
4.核心用法示例(对比WHERE和HAVING)
1. 按单字段分组:统计每组的聚集结果
例:查询每个学生的平均成绩(只显示平均≥90分的学生)
SELECT Sno,AVG(Grade)AS 平均分 -- 分组列是Sno,对每个Sno的Grade求平均FROM SC GROUPBY Sno -- 按学号分组,每个学号一组HAVINGAVG(Grade)>=90;-- 筛选出平均分≥90的组- 为什么不用WHERE?:
WHERE只能筛选「行」(比如Grade>80的行),但「平均分≥90」是针对整个组的条件,必须用HAVING。
2. 错误示例:在WHERE中用聚集函数
下面的写法是 错误的:
SELECT Sno,AVG(Grade)FROM SC WHEREAVG(Grade)>=90-- ❌ 错误!WHERE不能用聚集函数(AVG是分组后的统计结果)GROUPBY Sno;- 原因:
WHERE在分组 之前 执行,此时还没有分组结果,无法计算AVG(Grade)。 - 正确做法:用
HAVING在分组 之后 筛选组。
3.WHERE vs HAVING:核心区别
| 子句 | 作用对象 | 执行时机 | 能否用聚集函数 | 典型场景 |
|---|---|---|---|---|
| WHERE | 原始表中的行 | 分组 之前 | ❌ 不能 | 筛选单个行(如Grade<60的行) |
| HAVING | 分组后的组 | 分组 之后 | ✅ 能用 | 筛选组(如平均成绩<60的组) |
例子对比:
查「2023学期中,平均成绩<60分的课程组」(组级筛选):
GROUPBY Cno HAVINGAVG(Grade)<60查「成绩<60分且属于2023学期的行」(行级筛选):
WHERE Semester='2023'AND Grade<604.多字段分组:按多个列分组
例:按「课程号」和「学期」分组,统计每门课每学期的选课人数
SELECT Cno, Semester,COUNT(Sno)AS 选课人数 -- 分组列是Cno和SemesterFROM SC GROUPBY Cno, Semester;-- 同时按两个列分组,组内这两个列的值都相同- 结果:每个(课程号,学期)组合为一组,统计该组的选课人数。
5 练习(附正确答案)
练习1:查询平均分低于50分的课程的课程号及其平均分
需求分析:
- 按「课程号(Cno)」分组,计算每组的平均分。
- 筛选出平均分<50的组(组级条件,用HAVING)。
正确写法:
SELECT Cno,AVG(Grade)AS 平均分 FROM SC GROUPBY Cno -- 按课程号分组 HAVINGAVG(Grade)<50;-- 筛选平均分<50的组 - 注意:如果写成
WHERE AVG(Grade)<50会报错,因为WHERE不能用聚集函数。
练习2:查询2019学年不及格课程总数≥3门的学生,显示学号和不及格门数,按学号升序排序
需求分析:
- 行级筛选:先筛选出「2019学年(Semester含2019)」且「成绩<60(不及格)」的行。
- 分组:按「学号(Sno)」分组(每个学生一组)。
- 组内统计:统计每组的不及格课程数(用COUNT(*))。
- 组级筛选:筛选出不及格门数≥3的组(用HAVING)。
- 排序:按学号升序排列。
正确写法:
SELECT Sno,COUNT(*)AS 不及格门数 FROM SC WHERE Semester LIKE'2019%'AND Grade <60-- 行级筛选:2019学年且不及格 GROUPBY Sno -- 按学号分组 HAVINGCOUNT(*)>=3-- 组级筛选:不及格门数≥3 ORDERBY Sno ASC;-- 按学号升序排序(ASC可省略,默认升序) - 关键点:
WHERE先排除无关的行(比如非2019学年、成绩≥60的行),再分组统计,效率更高。
6.总结:
- 确定分组列:数据按哪列/哪些列分组(如Sno、Cno)。
- 行级筛选(可选):用WHERE过滤掉不需要的行(分组前执行)。
- 分组+统计:用GROUP BY分组,对每组用聚集函数(COUNT、AVG等)。
- 组级筛选(可选):用HAVING筛选不符合条件的组(必须用聚集函数时)。
- 排序(可选):用ORDER BY对最终结果排序。
3.1.6 LIMIT子句
1.LIMIT子句的作用
一句话总结:让查询结果只显示 指定数量的行,比如只看前10条数据,或第3-5条数据。
举个生活例子:
- 成绩单太长,只想看前10名?用
LIMIT 10。 - 分页查看数据(比如第2页,每页5条)?用
LIMIT 5 OFFSET 5(跳过前5条,取接下来的5条)。
2.基本语法与参数解析
SELECT 列名 FROM 表名 [WHERE 条件][ORDERBY 排序]LIMIT<数量>[OFFSET<跳过行数>];- 核心参数:
LIMIT <n1>:必选,指定要显示的 最大行数(从第1行开始取n1行)。OFFSET <n2>:可选,指定 跳过前面的n2行,再取后面的n1行(默认OFFSET 0,即不跳过)。
- 执行顺序:先执行
SELECT和WHERE,再排序(ORDER BY),最后用LIMIT限制行数。
3.常用场景与示例
3.1 取前N条数据(最常用)
例:查询成绩前10名的学生学号(按成绩降序,取前10行)
SELECT Sno FROM SC, Course WHERE Course.Cname ='数据库系统概论'AND SC.Cno = Course.Cno ORDERBY Grade DESC-- 先按成绩从高到低排序 LIMIT10;-- 只取前10行(第1-10名) - 作用:快速获取“Top N”数据(如Top 5销量、Top 3分数)。
3.2 分页查询(跳过前面的行,取中间部分)
例:查询平均成绩排名3-5名的学生(第3、4、5名)
SELECT Sno,AVG(Grade)AS 平均分 FROM SC GROUPBY Sno ORDERBY 平均分 DESC-- 先按平均分从高到低排序 LIMIT3OFFSET2;-- 跳过前2行(第1-2名),取接下来的3行(第3-5名) - 公式:第n页,每页m条数据 →
LIMIT m OFFSET (n-1)*m。- 比如第2页,每页5条:
LIMIT 5 OFFSET 5(跳过前5条,取第6-10条)。
- 比如第2页,每页5条:
3.3搭配分组和排序使用
练习:查询选课人数最多的前5名课程
SELECT Cno,COUNT(*)AS 选课人数 FROM SC GROUPBY Cno -- 按课程号分组,统计每组人数 ORDERBY 选课人数 DESC-- 按人数从多到少排序 LIMIT5;-- 只取前5名课程 - 逻辑:先分组统计,再排序,最后用LIMIT限制结果为前5条。
4.关键细节
OFFSET的省略:- 若只写
LIMIT n1,等价于LIMIT n1 OFFSET 0(从第1行开始取n1行)。 - 例:
LIMIT 5→ 取前5行;LIMIT 5 OFFSET 0→ 同上。
- 若只写
- 排序的重要性:
- 如果不先排序(
ORDER BY),LIMIT取的是数据库默认的无序结果(可能每次运行结果不同)。 - 最佳实践:
LIMIT几乎总是和ORDER BY一起使用,确保取到正确顺序的数据。
- 如果不先排序(
- 性能优势:
- 当数据量很大时,用
LIMIT可以避免加载全部数据,提升查询速度(比如只需要前10条,不用查全部10万条)。
- 当数据量很大时,用
5.总结:
| 需求场景 | 语法示例 | 说明 |
|---|---|---|
| 取前N条数据 | LIMIT N | 从第1行开始,取N行(如N=10,取前10行)。 |
| 取第M到第N条数据 | LIMIT N-M OFFSET M | 跳过前M行,取接下来的N-M行(如M=2,N=5,取第3-5行,用LIMIT 3 OFFSET 2)。 |
| 分页(第n页,每页m条) | LIMIT m OFFSET (n-1)*m | 例:第2页,每页5条 → LIMIT 5 OFFSET 5(跳过前5条,取第6-10条)。 |
| 取Top N数据(排序后) | ORDER BY 列 DESC LIMIT N | 先降序排序,再取前N条(如Top 5高分)。 |
以上就是这篇博客的全部内容,下一篇我们将继续探索更多精彩内容。
我的个人主页,欢迎来阅读我的其他文章
https://blog.ZEEKLOG.net/2402_83322742?spm=1011.2415.3001.5343
我的数据库系统概论专栏
https://blog.ZEEKLOG.net/2402_83322742/category_12911520.html?spm=1001.2014.3001.5482
| 非常感谢您的阅读,喜欢的话记得三连哦 |