下午写完了上一篇文章,晚上就开始做牛客的SQL题啦,然后发现少少的十题就用到了好几次多表连接,case在实际分析过程中也非常常用,所以我打算单独把多表查询和case拉出来写一篇文章。


文章目录:

  1. 表的加法
  2. 表的联结
  3. 联结案例
  4. case表达式

一、表的加法

表与表相加最简单的方法就是使用union/union all这两个关键字,其中union会在求并集的时候去除完全一致的记录行,而union all则会保留所有的行。

例如:新建一个course1表,分别使用union/union all将其与原有course表合并

先看course1:

be7e663158f174e9619bf79b78d30228.png

course:

bb5ff006f96f154dc88c7131c2b47629.png
course

union:

743eda808d3c2f79b2739637a47deecc.png

union all:

18856e046782084450a8ffa68e3458bf.png

但是前文说的"重复",是只看主键,还是一行里所有的字段都看呢?

我将course1里 "0001 语文 0002"暂时改成了"0001 语 0002",执行同样的union语句:

695a5ef6a0d454717bca7f34a80fd07e.png
使用union时,MySQL判断是否是重复行会比较所有的字段

注意事项:

  1. union(all)两侧的查询语句如果最终的输出列数量不一致,就会报错:

f525cc75c37beab1c53d1bd574b16482.png
错误信息:查询语句应该有数量一致的结果列

二、表的联结

为了降低数据库的冗余,使数据库好维护,也为了让相应的用户只看到自己该看的信息,我们在设计数据库时会尽量避免建立一个字段很庞杂的表。但实际使用的时候,我们往往需要结合多个表的信息一起看,这就要涉及到表的联结啦!

5b577a27de458fc60c9158eb2708852e.png

表的联结,往往是靠他们之间共有的列实现的:student(学号,姓名,性别),score(学号,课程号,成绩),这两个表就可以通过"学号"联结起来,查出同一个学生学习了哪些课程,成绩如何。

cross join(求笛卡尔积)由于使用频率不高,在此先略过。

①inner join 内连接:

运行示意图:

70cbde37a02056f4fdbce64505b9bd3b.png

SQL示例:

select column1,column2,...column n

from table1 inner join table2

on table1.共有列=table2.共有列

where condition...;

②left join 左连接:

运行示意图:

8c7da7dec69a050556678dd937b25b69.png

SQL示例:

select column1,column2,...column n

from table1 left join table2

on table1.共有列=table2.共有列

where condition...;

但!是!想求出下图的红色块怎么办?

ec6a3f3ad75d1488accee3cf86a3bc76.png

左连接由于保留了左表的所有记录,因此在右表做匹配时可能会存在没有值能和左表匹配上的情况。此时在联结出来的表中,有些记录的右表字段都是NULL。

NULL这一特征,就是我们破题的题眼→只要我们用where找出联结表中右表字段(多用的是左右两表的共有字段)为NULL的值,那么问题就解决了

SQL示例:

select * from table1 left join table2

on table1.sharedcolumn=table2.sharedcolumn

where table2.sharedcolumn IS NULL;→不要忘了判断是否为空用的关键字是 IS (NOT) NULL,而不是'=NULL'

③right join 右联结

677bd40150d77cdb2b59210c5d9a516d.png

SQL示例:

select column1,column2,...column n

from table1 right join table2

on table1.共有列=table2.共有列

where condition...;

和left join 一样的问题:怎么求出红色块

4b83451376cd12a51f367ab5c1a91f93.png

只要我们用where找出联结表中左表字段(多用的是左右两表的共有字段)为NULL的值,问题就解决了,具体SQL代码见上文。

注意:左右表联结由于只有一个表保留了所有记录行,因此有主次表之分。而主次表这样的情况在实际情况中非常常见,在此补充一个我做题时遇到的两个案例

f8b5d45566e41d5f275c82884159b63f.png
图中的SQL已经通过了系统的检查

7a4ac871e9bc5cf4caab8a4b75250e08.png

③full join 全连接

MySQL不支持full join,所以在这里不多展开了。

三、联结实用案例

①求各学生的学号,姓名,选课数以及总成绩

87b56ab6f09ccc22e6d260db0cbdef4f.png
由于要统计所有学生的信息,所以无论他是否选课,都应该被呈现在结果里。故应该以student表为主表做left join

②求平均成绩超过85的所有学生的学号,姓名,平均分

eefcbbcade26e0d0c566c91cc62dfc11.png
由于题目问的是平均成绩超过85的学生,因此没有选课的学生就不必考虑了。在此可以用score做主表left join student,也可以直接inner join

③求各个学生的学号,姓名,选修课程号,课程名称

97a241ad97e3d0bb2532878c9706e6f2.png
由于是求所有学生的选课情况,因此无论有没有选课都应该被包含在结果里,故应将student做为主表且保留所有记录,即以student为主表left join其他表。

四、case表达式

case表达式就像是中文中的固定搭配:如果...就..否则...,它可以实现许多复杂的判断条件。

SQL结构:

case(

when 列名 判断条件1 then 满足判断条件的操作1

when 列名 判断条件2 then 满足判断条件的操作2

...

ELSE 以上所有判断条件都不满足时对应的操作

END)

常用场景:

①做为查询结果中的一列,例如:

select 学号,(case 性别=‘F' '女' else ‘男' END) as 性别

from student;

②做聚合函数括号内的组成部分

sum(case avg(成绩)>60 then 1

else 0

END) as 及格人数

③做查询范围

④修改某列的值

update 学生

set 性别=

(case 性别='M' '男' else '女' end);

应用案例:查询每门课有多少学生及格,多少同学挂科

select 课程号,sum(case when 成绩>60 then 1 else 0 end) as 及格人数,count(*)-及格人数 as 不及格人数

from score

group by 课程号;

注意事项:case表达式中else可以省略,但end不行。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐