数据库系统概论(八)SQL单表查询语言超详细讲解(附带例题表格对比带你一步步掌握)

数据库系统概论(八)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;
总结:
  1. 精确比较(等于、大于等):用 =、>、< 等符号。
  2. 区间范围(20-23岁):用 BETWEEN ANDNOT BETWEEN AND
  3. 多个指定值(两个专业):用 INNOT IN
  4. 模糊匹配(姓刘、学号开头):用 LIKE,搭配 %(任意长度)、_(单个字符),特殊字符用 ESCAPE 转义。
  5. 空值处理(有无成绩):用 IS NULLIS NOT NULL
  6. 多个条件组合:用 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;
  • 结果
    1. 课程号(Cno)从小到大排列。
    2. 若课程号相同(比如都是81003),则按成绩(Grade)从高到低排列。
5.注意事项
  1. 空值(NULL)的排序
    • 不同数据库系统处理不同,常见两种:
      • NULL值排在最前(如MySQL)。
      • NULL值排在最后(如SQL Server)。
    • 若想控制NULL的位置,可结合 IS NULLCOALESCE 函数处理。
  2. 排序性能
    • 对大量数据排序时,会消耗更多资源。
    • 尽量在排序字段上创建索引,提高速度。
6.总结:
  1. 常见场景
    • 成绩排名(降序)。
    • 时间线(最新的在前,降序)。
    • 字典序(姓名、商品名升序)。

多字段排序

ORDERBY 列1[ASC/DESC], 列2[ASC/DESC];-- 先按列1,再按列2

单字段排序

ORDERBY 列名 ASC;-- 升序(默认)ORDERBY 列名 DESC;-- 降序

3.1.4 聚集函数

1.什么是聚集函数?

一句话总结:聚集函数专门用来 “批量计算” 数据,比如统计有多少行、求和、求平均等。
举个生活例子:

  • 想知道全班有多少人?用 COUNT
  • 想算全班数学平均分?用 AVG
  • 想找最高分、最低分?用 MAXMIN
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 的学生选修课程的总学分(需要连接 SCCourse 表,因为学分在 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.关键细节
  1. NULL值的处理
    • COUNT(*):不管有没有 NULL,只要有行就统计。
    • COUNT(列名)SUMAVGMAXMIN:自动忽略 NULL(当列值是 NULL 时,不参与计算)。
  2. DISTINCT 和 ALL
    • 默认为 ALL(计算所有值,包括重复的)。
    • DISTINCT 表示去重后再计算,比如 COUNT(DISTINCT Sno) 只算不同的学生。
  3. 多表使用
    • 如果要统计的列在多个表中,需要先通过 表连接(比如 SCCourse 用课程号连接),再用聚集函数。
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 列求和。
    • MAXMINAVG 三个函数一起统计。

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.总结:
  1. 数数量
    • 所有行:COUNT(*)
    • 某列非空值:COUNT(列名)
    • 去重后数量:COUNT(DISTINCT 列名)
  2. 算总和/平均(仅限数值列):SUM(列名)AVG(列名)
  3. 找最值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.关键逻辑步骤:
  1. 先过滤行:用 WHERE 筛选符合条件的行(还没分组时就过滤)。
  2. 再分组:按 GROUP BY 的列把数据分成多个组(同组的分组列值相同)。
  3. 组内统计:对每个组用聚集函数(如 AVG(Grade)COUNT(*))计算。
  4. 筛选组:用 HAVING 筛选掉不符合条件的组(比如平均成绩<60分的组)。
  5. 最后排序:用 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<60
4.多字段分组:按多个列分组

例:按「课程号」和「学期」分组,统计每门课每学期的选课人数

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门的学生,显示学号和不及格门数,按学号升序排序
需求分析

  1. 行级筛选:先筛选出「2019学年(Semester含2019)」且「成绩<60(不及格)」的行。
  2. 分组:按「学号(Sno)」分组(每个学生一组)。
  3. 组内统计:统计每组的不及格课程数(用COUNT(*))。
  4. 组级筛选:筛选出不及格门数≥3的组(用HAVING)。
  5. 排序:按学号升序排列。

正确写法

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.总结:
  1. 确定分组列:数据按哪列/哪些列分组(如Sno、Cno)。
  2. 行级筛选(可选):用WHERE过滤掉不需要的行(分组前执行)。
  3. 分组+统计:用GROUP BY分组,对每组用聚集函数(COUNT、AVG等)。
  4. 组级筛选(可选):用HAVING筛选不符合条件的组(必须用聚集函数时)。
  5. 排序(可选):用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<跳过行数>];
  • 核心参数
    1. LIMIT <n1>:必选,指定要显示的 最大行数(从第1行开始取n1行)。
    2. OFFSET <n2>:可选,指定 跳过前面的n2行,再取后面的n1行(默认OFFSET 0,即不跳过)。
  • 执行顺序:先执行 SELECTWHERE,再排序(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条)。
3.3搭配分组和排序使用

练习:查询选课人数最多的前5名课程

SELECT Cno,COUNT(*)AS 选课人数 FROM SC GROUPBY Cno -- 按课程号分组,统计每组人数 ORDERBY 选课人数 DESC-- 按人数从多到少排序 LIMIT5;-- 只取前5名课程 
  • 逻辑:先分组统计,再排序,最后用LIMIT限制结果为前5条。
4.关键细节
  1. OFFSET的省略
    • 若只写 LIMIT n1,等价于 LIMIT n1 OFFSET 0(从第1行开始取n1行)。
    • 例:LIMIT 5 → 取前5行;LIMIT 5 OFFSET 0 → 同上。
  2. 排序的重要性
    • 如果不先排序(ORDER BY),LIMIT 取的是数据库默认的无序结果(可能每次运行结果不同)。
    • 最佳实践LIMIT 几乎总是和 ORDER BY 一起使用,确保取到正确顺序的数据。
  3. 性能优势
    • 当数据量很大时,用 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
非常感谢您的阅读,喜欢的话记得三连哦
在这里插入图片描述

Read more

动态规划——01背包问题

01背包: 一.二维数组实现01背包 包括物品和背包,但每个物品的数量只有一个,所以只需要考虑选一个和不选两种情况。 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 在下面的讲解中,我举一个例子: 背包最大重量为4。 物品为: 重量价值物品0115物品1320物品2430 问背包能背的物品最大价值是多少? 1.确定dp数组以及下标的含义 二维dp数组,通过上面的表格和所求可以得出,需要两个维度分别表示物品和背包容量。 dp[i][j]其中i表示物品,j表示背包容量,dp[i][j]表示从下标为[0-i]的物品里面任意取,放进容量为j的背包,价值总和最大是多少。所以根据上述含义,则dp[1][4]表示任取 物品0,物品1 放进容量为4的背包里,最大价值是 dp[1][4]

By Ne0inhk
【初阶数据与算法】线性表之顺序表的定义与实现

【初阶数据与算法】线性表之顺序表的定义与实现

文章目录 * 一、线性表的概念 * 二、顺序表 * 1.概念与结构 * 2.顺序表的分类 * 静态顺序表 * 动态顺序表 * 三、顺序表的实现 * 1.顺序表的结构 * 2.顺序表的初始化和销毁 * 初始化函数 * 销毁函数 * 3.顺序表的扩容 * 4.顺序表的尾插和头插 * 尾插函数 * 头插函数 * 5.顺序表的尾删和头删 * 尾删函数 * 头删函数 * 6.顺序表的查找 * 7.指定位置插⼊/删除数据 * 指定位置之前插入 * 指定位置删除数据 一、线性表的概念 线性表(linearlist)是n个具有相同特性的数据元素的有限序列,线性表在物理结构上并不⼀定是连续的,在逻辑结构上是连续的 物理结构就是在存储数据时真实的内存存储位置,物理结构上连续就是说在存储数据时,使用的是一段连续的内存空间存储数据,例如数组,它开辟出来的内存空间就是连续不断的,我们在数组的章节也做了介绍,

By Ne0inhk
链表经典OJ问题详解

链表经典OJ问题详解

文章目录 前言 1. 删除链表中等于给定值 val 的所有结点 2. 反转一个单链表 3. 链表的中间结点 4. 链表中倒数第k个结点 5. 合并两个有序链表 6. 链表分割 7. 链表的回文结构 8. 相交链表 9. 判断链表中是否有环 10. 返回链表开始入环的第一个结点 结语 前言 链表是一种基础且重要的数据结构,在程序设计中有着广泛的应用。由于其物理存储的非连续性,链表在插入、删除操作上具有独特的优势,但也给某些操作(如随机访问)带来了挑战。在技术面试和算法竞赛中,链表相关的题目出现频率极高,熟练掌握链表的常见操作和经典问题的解法,是每个程序员必备的技能。本文精选了10道经典的链表OJ题目,从思路分析到C语言代码实现,逐步详解,并穿插了快慢指针、哑结点等常用技巧的讲解,每道题目都附带了对应的在线练习链接,方便读者动手实践。希望能帮助读者深入理解链表,轻松应对各类链表问题。 1. 删除链表中等于给定值 val

By Ne0inhk
【优选算法必刷100题】第031~32题(前缀和算法):连续数组、矩阵区域和

【优选算法必刷100题】第031~32题(前缀和算法):连续数组、矩阵区域和

🔥艾莉丝努力练剑:个人主页 ❄专栏传送门:《C语言》、《数据结构与算法》、C/C++干货分享&学习过程记录、Linux操作系统编程详解、笔试/面试常见算法:从基础到进阶 ⭐️为天地立心,为生民立命,为往圣继绝学,为万世开太平 🎬艾莉丝的简介: 🎬艾莉丝的算法专栏简介: 目录 031  连续数组 1.1  解法一:暴力解法 1.2  解法二:前缀和在哈希表中 1.3  算法实现 1.3.1  C++实现 1.3.2  Java实现 1.4  博主手记 032  矩阵区域和 2.1

By Ne0inhk