概要:
文章主要围绕 SQL 相关知识展开,先介绍了 JOIN 与 LEFT JOIN 的区别及各自应用场景,接着阐述了窗口函数的定义、语法、类型、子句作用及应用场景,还提及了 CASE 与 IF 的使用。随后通过分数排名、体育馆人流量高峰期查询、统计总成绩等十个具体应用场景的 SQL 查询示例,详细讲解了怎样运用各类 SQL 函数、语句及操作来实现差别的查询需求,包罗排名计算、数据筛选、聚合统计、值的互换以及座位调解等多种功能。
目录
JOIN与LEFT JOIN的区别
窗口函数:
CASE与IF的使用
第一关:分数排名
表明:
第二关:体育馆的人流量
表明:
第三关:统计总成绩
第四关:查询学平生均分
表明:
COALESCE函数
第五关:查询修课相同弟子信息
表明:
第六关:查询各科成绩并排序
表明:
第七关:查询张老师课程成绩最高的弟子信息
表明:
第八关:查询两门课程不合格同学信息
表明:
第九关:互换工资
表明:
第十关:换座位
表明:
JOIN与LEFT JOIN的区别
- JOIN(内连接,通常指INNER JOIN)
- 定义:它返回的是两个表中满足连接条件的行的组合。只有当两个表中的行在连接条件(例如,基于某个共同的列进行相等比较)上匹配时,这些行才会出如今效果会合。
- 示例:假设有两个表,表A(包罗列A_id、A_name)和表B(包罗列B_id、B_name),通过A_id = B_id进行连接。
- 表 A 的数据如下: A_idA_name1张三2李四3王五
- 表 B 的数据如下: B_idB_name1赵六2孙七4周八
- 使用JOIN连接后的效果如下: A_idA_nameB_idB_name1张三1赵六2李四2孙七
- 表明:只有在A_id和B_id相等的行((1,张三)与(1,赵六),(2,李四)与(2,孙七))才会被包罗在效果会合,而表A中的(3,王五)和表B中的(4,周八)因为没有与之匹配的行,以是不会出如今效果中。
- 应用场景:当你只关心两个表中相互关联的数据,并且希望去除没有关联的数据时,JOIN是一个很好的选择。例如,查询购买了商品的顾客信息和他们购买的商品信息,只需要关联顾客表和购买记录表中匹配的记录。
- LEFT JOIN(左连接)
- 定义:它返回左表(在LEFT JOIN关键字左边的表)中的全部行,以及右表(在LEFT JOIN关键字右边的表)中与左表满足连接条件的行。如果右表中没有匹配的行,那么对应的列将填充为NULL。
- 示例:使用上面的表A和表B,通过A_id = B_id进行LEFT JOIN。
- 效果如下: A_idA_nameB_idB_name1张三1赵六2李四2孙七3王五NULLNULL
- 表明:左表表A中的全部行都被返回。对于表A中的(1,张三)和(2,李四),因为在表B中有匹配的行,以是相应的B_id和B_name列有值。而对于表A中的(3,王五),由于在表B中没有B_id = 3的匹配行,以是B_id和B_name列填充为NULL。
- 应用场景:当你需要保留一个表(通常是主表)中的全部记录,并且获取与之相关(如果有的话)的另一个表中的记录时,LEFT JOIN很有用。例如,查询全部顾客信息以及他们购买的商品信息(如果有购买记录),纵然某个顾客没有购买商品,也要在效果中显示该顾客的根本信息。
窗口函数:
先表明一下什么是窗口函数,后面会用到。
- 定义
- 窗口函数是一种在 SQL 中用于对查询效果集的一部分进行计算的函数。它可以在不改变查询效果集行数的情况下,为每行数据计算一个聚合值或排名值等,就好像在数据上打开了一个 “窗口”,在这个窗口内进行计算。
- 语法布局
- 窗口函数的根本语法是函数名(参数) OVER (PARTITION BY 列名 [可选的排序条件])。
- 例如,SUM(sales) OVER (PARTITION BY region),这里SUM是函数名,sales是要进行求和的列,OVER是关键字,PARTITION BY region表现按照region列进行分区。
- 主要类型
- 聚合窗口函数:
- 包罗SUM(求和)、AVG(求均匀值)、MAX(求最大值)、MIN(求最小值)和COUNT(计数)等。这些函数在窗口内计算聚合值。
- 例如,有一个销售数据表,包罗product_id、sales_date和sales_amount列。如果想计算每个产品在差别日期范围内的销售总额,可以使用SUM(sales_amount) OVER (PARTITION BY product_id ORDER BY sales_date)。这将为每个产品(按照product_id分区)根据销售日期的次序,计算出每个日期对应的累计销售总额。
- 排名窗口函数:
- 如RANK、DENSE_RANK和ROW_NUMBER。
- RANK函数会根据指定的排序条件为行分配排名,相同的值会得到相同的排名,且排名会出现间断。例如,有一构成绩数据,当两个弟子成绩相同并列第一时,下一名弟子的排名是第三。
- DENSE_RANK函数也会为相同的值分配相同的排名,但排名是连续的。在上述成绩例子中,两个弟子并列第一后,下一名弟子的排名是第二。
- ROW_NUMBER函数会为每一行分配一个唯一的、连续的排名,不考虑值是否相同。
- PARTITION BY 子句
- 用于将效果集划分成多个分区,窗口函数会在每个分区内独立进行计算。
- 以员工表为例,包罗department_id(部分编号)、employee_id(员工编号)和salary(工资)列。如果使用AVG(salary) OVER (PARTITION BY department_id),就会为每个部分(按照department_id分区)计算均匀工资。如许,在查询效果中,每个员工记录旁边都会显示其地点部分的均匀工资。
- ORDER BY 子句(在窗口函数中)
- 用于指定每个分区内的排序次序。这个排序次序会影响窗口函数的计算效果,特别是对于一些需要次序信息的函数,如排名函数和累计计算函数。
- 例如,在计算累计销售额时,如果按照日期排序,就可以得到从最早日期到当前日期的累计销售额变革情况。如SUM(sales) OVER (PARTITION BY region ORDER BY sales_date),先按照region分区,然后在每个分区内按照sales_date排序来计算累计销售额。
- 应用场景
- 数据排名:如前面提到的成绩排名、销售业绩排名等。可以快速确定每个数据点在整个数据会合的相对位置。
- 数据聚合分析:计算每个分组内的累计统计信息,如累计销售额、累计产量等。可以帮助分析数据的增长趋势和分布情况。
- 数据对比:例如,将每个员工的工资与地点部分的均匀工资进行对比,从而相识员工工资在部分内的水平。
CASE与IF的使用
- SELECT
- CASE ###如果
- WHEN sex='1' THEN '男' ###sex='1',则返回值'男'
- WHEN sex='2' THEN '女' ###sex='2',则返回值'女'
- ELSE '其他' ###其他的返回'其他’
- END ###结束
- FROM sys_user ###整体理解: 在sys_user表中如果sex='1',则返回值'男'如果sex='2',则返回值'女' 否则返回'其他’
复制代码
- SELECT IF(sex='1','男','女') AS sex FROM sys_user; ###如果sex='1'则返回值'男' 否则返回值为'女'
复制代码 第一关:分数排名
编写SQL查询来实现二种排名方式的分数排名。
score表布局信息如下:
如果两个分数相同,则两个分数排名(Rank)相同。
- 情况一:平分后的下一个名次是下一个连续的整数值。换句话说,名次之间不应该有“隔断”。例:1、1、2、3、4、4。
- 情况二:排名是非连续的。例:1、1、1、4、4、6。
代码如下:
- SELECT Score,DENSE_RANK() OVER (ORDER BY Score DESC) AS rank
- FROM score;
- SELECT Score,RANK() OVER (ORDER BY Score DESC) AS rank
- FROM score;
复制代码 表明:
- 窗口函数部分
- DENSE_RANK() OVER (ORDER BY Score DESC):这是一个窗口函数。DENSE_RANK函数用于计算排名,它的特点是排名是连续的。OVER关键字用于指定窗口函数的作用范围,在这里(ORDER BY Score DESC)表现按照Score列的值进行降序排序来计算排名。
- RANK() OVER (ORDER BY Score DESC):同样是一个窗口函数。RANK函数用于计算排名,当遇到相同的值时,这些相同的值会得到相同的排名,但是下一个排名会出现间隙。OVER关键字的作用和之前一样,按照Score列降序排序来计算排名。
- 查询部分
- SELECT Score,DENSE_RANK() OVER (ORDER BY Score DESC) AS rank FROM score;:从score表中查询数据。选择Score列,并且使用DENSE_RANK窗口函数计算排名,将排名效果作为新的列rank(通过AS关键字命名)与Score列一起显示出来。
- SELECT Score,RANK() OVER (ORDER BY Score DESC) AS rank FROM score;:和第一段代码的查询部分雷同,从score表中查询Score列,同时使用RANK窗口函数计算排名并将效果作为rank列与Score列一起展示。
第二关:体育馆的人流量
某市建了一个新的体育馆,每日人流量信息被记录在gymnasium表中:序号 (id)、日期 (date)、 人流量 (visitors_flow)。
请编写一个查询语句,找出人流量的高峰期。高峰期时,至少连续三行记录中的人流量不少于100。
gymnasium表布局数据如下:
代码如下:
- SELECT DISTINCT g.id,g.date,g.visitors_flow
- FROM gymnasium g
- JOIN gymnasium g2 ON g.id BETWEEN g2.id AND g2.id + 2
- WHERE(
- SELECT COUNT(*)
- FROM gymnasium g3
- WHERE g3.id BETWEEN g2.id AND g2.id + 2
- AND g3.visitors_flow >= 100
- ) >= 3
- ORDER BY g.id;
复制代码 表明:
- 数据连接(JOIN 操作):
- 将 gymnasium 表与自身连接。连接条件 g.id BETWEEN g2.id AND g2.id + 2 表现对于 g 表中的每一行,其 id 要在 g2 表中某一行的 id 到这一行 id 加上 2的范围内。如许能通过排名差值判断两行数据是否连续,以便后续找出连续的行组合进行判断和筛选。
- 筛选条件(WHERE 子句):
- 这是一个关键条件,通过一个子查询来统计在 g2 表中与当前行数据连续的 2个范围内(即包罗当前行共 3 行),人流量凌驾 100 的记录数目。只有当这个数目大于等于 3 时,才说明存在连续三天以上人流量凌驾 100 的情况。
第三关:统计总成绩
计算每个班的语文总成绩和数学总成绩,要求科目中低于60分的成绩不记录总成绩。
tb_score布局数据:
tb_class表布局数据:
代码如下:
- SELECT classname,
- SUM(CASE WHEN chinese>=60 THEN chinese ELSE 0 END) chinese,
- SUM(CASE WHEN maths>=60 THEN maths ELSE 0 END) maths
- FROM tb_score JOIN tb_class ON name = stuname
- GROUP BY classname;
复制代码 这题比较简单,主要是使用SUM求和同时使用CASE判断一下成绩是否低于60分,如果低于60的话该成绩为设为0,如许就不会影响总成绩。
第四关:查询学平生均分
根据提供的表和数据,查询均匀成绩小于60分的同学的弟子编号(s_id)、弟子姓名(s_name)清静均成绩(avg_score),要求均匀成绩保留2位小数点。(留意:包罗有成绩的和无成绩的)
student表数据:
course表数据:
teacher表数据:
score表部分数据:
代码如下:
- SELECT student.s_id,
- student.s_name,
- CASE WHEN ROUND(COALESCE(AVG(score.s_score),0),2) = 0
- THEN ROUND(COALESCE(AVG(score.s_score),0),0)
- ELSE ROUND(COALESCE(AVG(score.s_score),0),2)
- END AS avg_score
- -- 需要保留成绩为null的数据,因此需要采用左连接,保存左边表的全部信息
- FROM student LEFT JOIN score ON student.s_id = score.s_id
- GROUP BY student.s_id,student.s_name
- HAVING avg_score<60
复制代码 表明:
这个题目关键在于两个地方,第一:怎样让两个表连接之后生存为值为NULL的数据;第二:怎样处置惩罚值为NULL的数据。
第一个题目:使用开头提到的LEFT JOIN,如许会保留student表中的全部数据,如果一个弟子没有成绩的话,连接后score一栏会为NULL,被生存下来。如果只是使用JOIN的话,如果弟子没有成绩,那么该弟子不会保留在连接后的表中,导致丢失我们需要的数据。
第二个题目:使用COALESCE函数。
COALESCE函数
- 定义
COALESCE是一个在 SQL 中用于处置惩罚 NULL 值的函数。它担当一个或多个参数,并返回第一个非 NULL 参数的值。如果全部参数都是 NULL,则返回 NULL。
- 语法和参数
语法格式通常为 COALESCE(expression1, expression2, expression3,...)。此中,expression1、expression2、expression3 等是要检查的表达式,可以是列名、常量或通过其他函数计算得到的值。这些参数会按照从左到右的次序进行检查。
COALESCE函数可用于处置惩罚可能出现的 NULL 值情况。在 COALESCE(AVG(score.s_score),0) 中,它的参数是 AVG(score.s_score) 和 0。这里是先实验计算 score 表中每个弟子的均匀成绩(通过 AVG(score.s_score)),如果某个弟子在 score 表中没有成绩记录,那么 AVG(score.s_score)对于该弟子就会返回 NULL,然后接着检查下一个参数,发现是0,而不是NULL,那么0就被返回了,如许的话就相称与将NULL值更换为了0,如许就可以确保在后续计算均匀成绩时,纵然弟子没有成绩记录,也能到场到均匀成绩的计算中,只是其均匀成绩会被计算为0.
Tips:对于为什么有一个与0比较:因为末了的预期输出中只有0不保留小数位,因此把0作为一个特殊情况提出来
第五关:查询修课相同弟子信息
根据提供的表和数据,查询与s_id=01号同学学习的课程完全相同的其他同学的信息(学号s_id,姓名s_name,性别s_sex)。
student表数据:
course表数据:
teacher表数据:
score表部分数据:
代码如下:
- WITH CourseList AS (
- SELECT
- c_id
- FROM
- score
- WHERE
- s_id = '01'
- )
- SELECT
- s.s_id,
- s.s_name,
- s.s_sex
- FROM
- student s
- JOIN score sc ON s.s_id = sc.s_id
- JOIN CourseList cl ON sc.c_id = cl.c_id
- GROUP BY
- s.s_id,
- s.s_name,
- s.s_sex
- HAVING
- COUNT(DISTINCT sc.c_id) = (SELECT COUNT(*) FROM CourseList)
- AND s.s_id != '01'
- ORDER BY s.s_id DESC
复制代码 表明:
1. 公共表表达式(CTE)部分:
- 这里使用了 WITH 关键字定义了一个公共表表达式(Common Table Expression,简称 CTE),名为 CourseList。
- 它的作用是从 score 表中筛选出 s_id 等于 01 的弟子所学的课程 c_id,形成一个暂时的课程列表。这个暂时列表将在后续的主查询部分用于比较其他弟子所学课程是否与之完全相同。
2. 主查询部分:
- 数据连接(JOIN 操作):
- student s JOIN score sc ON s.s_id = sc.s_id:首先通过 JOIN 操作将 student 表和 score 表基于弟子编号(s_id)进行连接。如允许以将弟子的根本信息(来自 student 表)与他们的课程成绩信息(来自 score 表)关联起来,以便后续可以大概获取到每个弟子所学课程的详细情况。
- JOIN CourseList cl ON sc.c_id = cl.c_id:接着,将上述连接后的效果再与之前定义的 CourseList CTE 进行连接,连接条件是课程编号(c_id)相等。这一步的目标是筛选出那些所学课程在 s_id = 01 的弟子所学课程列表中的记录,也就是找出与 s_id = 01 弟子有共同课程学习履历的弟子记录。
- 分组操作(GROUP BY 子句):
- GROUP BY s.s_id, s.s_name, s.s_sex:按照弟子编号(s_id)、弟子姓名(s_name)和弟子性别(s_sex)对连接后的效果进行分组。如许做是为了将每个弟子的全部相关记录(包罗与差别课程对应的成绩记录等)聚合在一起,以便后续可以大概针对每个弟子进行整体的统计和判断。
- 筛选条件(HAVING 子句):
- COUNT(DISTINCT sc.c_id) = (SELECT COUNT(*) FROM CourseList):在每个分组内(即每个弟子),通过 COUNT(DISTINCT sc.c_id) 统计该弟子所学的差别课程的数目。然后将这个数目与 (SELECT COUNT(*) FROM CourseList) 进行比较,后者是 s_id = 01 的弟子所学的课程总数。只有当这两个数目相等时,才说明当前分组所代表的弟子所学课程与 s_id = 01 的弟子所学课程完全相同。
- AND s.s_id!= '01':这个条件是为了排除 s_id = 01 本身,因为我们要查询的是与 s_id = 01 弟子学习课程完全相同的其他弟子的信息,以是不需要把 s_id = 01 的弟子再次包罗在效果中。
- 排序操作(ORDER BY 子句):
- ORDER BY s.s_id DESC:末了,按照弟子编号(s_id)进行降序排序。
第六关:查询各科成绩并排序
根据提供的表和数据,查询各科成绩,进行排序并显示排名,按弟子编号(s_id)、课程编号(c_id)、弟子成绩(s_score)和排名(rank)进行输出,具体效果请查看测试集。
student表数据:
course表数据:
teacher表数据:
score表部分数据:
代码如下:
- SELECT
- sc.s_id,
- sc.c_id,
- sc.s_score,
- DENSE_RANK() OVER (PARTITION BY sc.c_id ORDER BY sc.s_score DESC) AS rank
- FROM score sc
- ORDER BY sc.c_id,rank;
复制代码 表明:
这里用到了上面提到的DENSE_RANK来进行排名 ,忘记的话可以到上面看一看,这里对c_id进行分组(预期输出按照c_id分组了)然后按照s_score降序排序末了得到rank值。
第七关:查询张老师课程成绩最高的弟子信息
根据提供的表和数据,查询选修“张三”老师所授课程的弟子中,成绩最高的弟子信息(具体输出信息请查看测试说明)及其成绩。
student表数据:
course表数据:
teacher表数据:
score表部分数据:
代码如下:
- WITH ZSCourse AS (
- SELECT c_id
- FROM course
- WHERE t_id = (
- SELECT t_id
- FROM teacher
- WHERE t_name = '张三'
- )
- )
- SELECT s.s_id,s.s_name,s.s_sex,sc.s_score,sc.c_id,c.c_name
- FROM student s
- JOIN score sc ON s.s_id = sc.s_id
- JOIN course c ON sc.c_id = c.c_id
- JOIN ZSCourse zsc ON zsc.c_id = sc.c_id
- WHERE sc.s_score = (
- SELECT MAX(sc1.s_score)
- FROM score sc1
- WHERE sc1.c_id = zsc.c_id
- )
复制代码 表明:
1. 公共表表达式(CTE)部分:
- 这里使用了 WITH 关键字定义了一个公共表表达式(CTE),名为 ZSCourses。
- 它的作用是从 course 表中筛选出由 “张三” 老师所授课程的课程编号(c_id)。首先在 teacher 表中通过 t_name = '张三' 找到对应的 t_id,然后在 course 表中根据这个 t_id 筛选出相应的课程编号。这个暂时的课程编号列表将在后续的主查询部分用于筛选选修这些课程的弟子记录。
2. 主查询部分:
- 数据连接(JOIN 操作):
- 首先通过 JOIN 操作将 student 表和 score 表基于弟子编号(s_id)进行连接。如允许以将弟子的根本信息(来自 student 表)与他们的课程成绩信息(来自 score 表)关联起来,以便后续可以大概获取到每个弟子的完整信息。
- 接着,将上述连接后的效果再与之前定义的 ZSCourses CTE 进行连接,连接条件是课程编号(c_id)相等。这一步的目标是筛选出那些选修了 “张三” 老师所授课程的弟子记录。
- 末了再与 course 表进行连接,连接条件同样是课程编号(c_id)相等。这是为了获取到每门课程的名称(c_name),以便在终极查询效果中展示完整的课程信息。
- 筛选条件(WHERE 子句):
- 这里的关键在于找到选修 “张三” 老师所授课程的弟子中的最高成绩。通过一个子查询,找到score表中c_id与zsc中c_id相等的数据,然后使用 MAX(sc1.s_score) 函数计算出这些弟子中的最高成绩。主查询中的 WHERE 子句则是筛选出成绩等于这个最高成绩的弟子记录,如许就确保只获取到选修 “张三” 老师所授课程的弟子中成绩最高的弟子信息及其成绩。
第八关:查询两门课程不合格同学信息
根据提供的表和数据,查询两门及其以上不合格课程的同学的学号(s_id)、姓名(s_name)及其均匀成绩(avg_score),要求计算均匀成绩后为整数。
student表数据:
course表数据:
teacher表数据:
score表部分数据:
代码如下:
- SELECT s.s_id,s.s_name,ROUND(AVG(sc.s_score)) AS avg_score
- FROM student s JOIN score sc ON s.s_id = sc.s_id
- GROUP BY s.s_id,s.s_name
- HAVING(
- SELECT COUNT(*)
- FROM score sc2
- WHERE sc2.s_score<60 AND sc2.s_id = s.s_id
- )>=2;
复制代码 表明:
这里就简单表明一下HAVING语句,相信前面的大家都能明确!
HAVING语句中COUNT用于统计不合格的课程数,然后设置了两个统计的条件:第一:sc2.s_score<60用与筛选出小于60分的课程;第二: sc2.s_id = s.s_id用于保是针对当前分组(也就是当前弟子)的成绩记录进行统计。
第九关:互换工资
给定一张 tb_Salary 表,如下所示,有 m = 男性 和 f = 女性的值。互换全部的 f 和 m 值(例如,将全部 f 值更改为 m,反之亦然)。
要求只使用一句更新update语句,且不允许含有任何select语句完成任务。
代码如下:
- UPDATE tb_Salary
- SET sex = CASE WHEN sex = 'f' THEN 'm' ELSE 'f' END;
复制代码 表明:
这题只要掌握了CASE或者IF的用法,便可以轻松办理,关于CASE和IF,在文章开头提到了,如果忘记的话可以回去看看。
第十关:换座位
小美是一所中学的信息科技老师,她有一张 tb_Seat座位表,寻常用来储存弟子名字和与他们相对应的座位 id。
tb_Seat表布局数据如下:
如今小美想改变相邻俩弟子的座位(若弟子人数为奇数,则无需改变末了一位同学的座位),如今需要你编写SQL输出小美想要的的效果。
代码如下:
- SELECT
- CASE
- WHEN id%2 = 1 AND id<(SELECT MAX(id) FROM tb_Seat) THEN id+1
- WHEN id%2 = 0 AND id>1 THEN id-1
- ELSE id
- END
- id,name
- FROM tb_Seat
- ORDER BY id;
复制代码 表明:
这题如果想要互换相邻弟子的姓名的话,可能不太好做,那么我们换一个思路,是不是可以互换id,然后按照id进行排序呢?如许是可以的,因为id是数值类型,可以通过加一或者减一的操作进行互换,比互换弟子的姓名就简单多了。那么这里我们也需要用到CASE语句,通过判断id是奇数还是偶数决定id是加一还是减一(奇数加一,偶数减一)就可以实现互换id的操作,然后还需要留意如果总人数为奇数的话末了一名弟子不互换,以是我们还需要比较id为奇数时该弟子是不是末了一名(第一个WHEN中的AND),如果时末了一名的话id保持不变(ELSE),末了按照id排序就可以了。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|