前言❤️❤️
hello hello💕,这里是洋不写bug~😄,欢迎大家点赞👍👍,关注😍😍,收藏🌹🌹
联合查询和连接查询是学习数据库的必修课,这部分的使用率超级高,后面学习的查询很多也都会用到联合查询,这部分铁汁们一定要多练习💪
🎇个人主页:洋不写bug的博客
🎇所属专栏:数据库
🎇mysql8.0和navicate的安装:mysql安装教程
🎇铁汁们对于MySQL数据库的各种常用核心语法,都可以在上面的数据库专栏学习,专栏正在持续更新中🐵🐵,有问题可以写在评论区或者私信我哦~
1,基础联合查询
在数据库中有这样的两张表相互关联(如下图)

如果想要得到一张学生表和班级表的整合,同时又没有班级编号(如下图),这就要用到联合查询(这里的联合指的是多个表的组合)

1,查询所需表的笛卡尔积
查询的所有表取笛卡尔积,结果集存在临时表中
先创建出这两个表,插入一下数据
sql
create table class(
id int PRIMARY KEY,
name VARCHAR(20)
)
create table students(
id bigint PRIMARY key AUTO_INCREMENT,
name VARCHAR(16),
gender VARCHAR(3),
class_id int,
FOREIGN key(class_id) REFERENCES class(id)
)
insert into class(id,name) VALUES(1,"Java班"),(2,"C++班"),(3,"python班")
insert into students(name,gender,class_id) VALUES("张三","男",1),("李四","女",2),("王五","女",2)
所谓的笛卡尔积就是两个表里面的内容都组合一遍,形成一个表,就是对两个表都进行全部查询,如下:
sql
SELECT * from students,class

2,用Where进行数据过滤
查出所有笛卡尔积后,接下来就是用Where条件对数据进行过滤(如下图)
用表名.列名的方式表示数据

3,精简查询(起别名简化)
最终想要的表是这样一个表,里面是没有班级id的(如下图),这就需要指定列进行查询,但是指定列的话每次都写表的全名就太麻烦了😅

我们可以通过给表起别名的方式,来简化sql语句(如下图)
这样指定列名查询,查询出的表的效果就非常好,同时又不用每次都写很长的表名
(注:班级名的name后面括号里面加了个1,是为了跟前面学生名字的name区分开)

2,内连接
在联合查询中Where过滤数据时,还可以搭配AND,进一步进行精准查询(如下图),查询张三的班级信息:

这种Where条件查询时加个AND,就属于是隐式内连接,隐式内连接是连接的一种
还有一种是显示内连接
语法:select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 where其他条件
例如还是查询张三的班级信息,显式外连接查询如下(inner可写可不写):

显示内连接和隐式内连接各有各的好处,铁汁们初学根据自己喜好使用即可
隐式内连接若遗漏WHERE中的连接条件,会直接产生两个表的笛卡尔积(数据量爆炸);而显式连接必须通过ON指定连接条件,语法层面就减少了这种错误显示内连接的逻辑也更容易看出来
前面提到的只是内连接的基础部分,这里就创建四个表course,class,student,score(course,student,score三个表之间的关系在上篇数据库设计博客的多对多关系那里提到过,感兴趣的铁汁可以看下)来深度学习下内连接
sql
DROP TABLE if exists course;
create TABLE course(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20),
);
DROP TABLE if exists class;
create TABLE class(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20)
);
DROP TABLE if exists student;
create TABLE student(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(20) not null,
sno VARCHAR(10) not null,
age int DEFAULT 18,
gender TINYINT(1),
enroll_date date,
class_id BIGINT
);
DROP table if EXISTS score;
create TABLE score(
id BIGINT PRIMARY KEY AUTO_INCREMENT,
score float,
student_id BIGINT,
course_id BIGINT
)
创建完成后开始给里面插入数据
sql
insert into course (name) values ('Java'), ('C++'), ('MySQL'),
('操作系统'), ('计算机网络'), ('数据结构');
insert into class(name) values('Java班'),('C++班'),('python班')
INSERT INTO student (id, name, sno, age, gender, enroll_date, class_id)
VALUES
(1, '唐三藏', '100001', 18, 1, '1986-09-01', 1),
(2, '孙悟空', '100002', 18, 1, '1986-09-01', 1),
(3, '猪悟能', '100003', 18, 1, '1986-09-01', 1),
(4, '沙悟净', '100004', 18, 1, '1986-09-01', 1),
(5, '宋江', '200001', 18, 1, '1986-09-01', 1),
(6, '武松', '200002', 18, 1, '2000-09-01', 2),
(7, '李逵', '200003', 18, 1, '2000-09-01', 2);
insert into score (score, student_id, course_id) values
(70.5, 1, 1),(98.5, 1, 3),(33, 1, 5),(98, 1, 6),
(60, 2, 1),(59.5, 2, 5),
(33, 3, 1),(68, 3, 3),(99, 3, 5),
(67, 4, 1),(23, 4, 3),(56, 4, 5),(72, 4, 6),
(81, 5, 1),(37, 5, 5),
(56, 6, 2),(43, 6, 4),(79, 6, 6),
(80, 7, 2),(92, 7, 6);
3个表创建完,数据插入完成后,就开始进行练习:
例如只查询唐三藏同学的成绩(显示出姓名,科目,成绩)
那还是前面的三步走
首先就是查询所需表的笛卡尔积,这里三个表都需要,这里一查,数据非常多

接着就是通过where筛选呢来过滤掉无效数据和精简查询了,就查询完成了
无效数据筛选:这里有两个筛选条件,一个是名字是唐三藏,一个是student表中的id和score表中的student_id必须相等
精简查询 :查询学生名字和成绩,并且用别名来进行简化如下:
sql
select s.name,c.name,sc.score
from student s,score sc,course c
where s.name = "唐三藏"
AND s.id = sc.student_id
AND sc.course_id = c.id

接下来就进阶一点
要求实现这样一个查询,求每个学生考试总成绩之和:
这个就要用到一个分组查询
看起来很复杂,其实还是三步走首先确定要实现这个查询需要哪些表,这里只需要score表和student表
(如果记不太清楚表之间的关系,可以用desc + 表名看下各个表的结构,再分析下它们之间的联系)
接着第二步,只是加上个分组计算就行了
以哪个表中的哪个列分组呢?
这里既然是求每个同学的成绩之和,那就是每个同学是一组
这个可以用student.id或者score.student_id进行分组(不要用学生名字进行分组,因为学生可能会出现重名)
如下:
sql
SELECT s.id,s.name,sum(sc.score) from student s,score sc WHERE
s.id = sc.student_id GROUP BY s.id

这里也可以改下列名,如下图:

要求:查询所有同学每门课的成绩,以及同学的个人信息
这个就要联立三张表来进行查询,这里如果记得不太清表的关系,直接看下表的结构来分析下:



这里student和course是通过score表连接在一起的
那就是student.id = score.student_id
course.id = score.course_id
sql语句也不难理解:
sql
SELECT s.id,s.name,s.sno,s.age,s.gender,s.enroll_date,c.name,sc.score
from student s,course c,score sc
WHERE s.id = sc.student_id AND c.id = sc.course_id

这里也可以用显示内连接来写(效果一样):
sql
SELECT s.id,s.name,s.sno,s.age,s.gender,s.enroll_date,sc.score,c.name,sc.score
from student s
join score sc on sc.student_id = s.id
join course c on sc.course_id = c.id
在工作中尽量少对大表进行关联查询 ,一些公司会要求关联的个数不能超过3个,这是为了防止数据量太大,性能崩溃
3,外连接
外连接分为左外连接、右外连接和全外连接三种类型,MySQL 不支持全外连接,这里就不学习了
- 左外连接:返回左表的所有记录和右表中匹配的记录。如果右表中没有匹配的记录,则结果集中对应字段会显示为 NULL
- 右外连接:与左外连接相反,返回右表的所有记录和左表中匹配的记录。如果左表中没有匹配的记录,则结果集中对应字段会显示为 NULL。
- 全外连接:结合了左外连接和右外连接的特点,返回左右表中的所有记录。如果某一边表中没有匹配的记录,则结果集中对应字段会显示为 NULL
右外连接使用时数据库也是会将其优化为左外连接,这里重点掌握左外连接即可
语法:
sql
select 字段名 from 表名1 left/right join 表名2 on 连接条件
使用左外连接时,表名1就是基准表,表名1完全显示
使用右外查询时,表名2就是基准表,表名2完全显示
那外连接的使用场景是什么呢,示例如下
在student表中插入一个id为8,名字为不想毕业的学生,这个学生所有考试都没有参加😅
sql
insert into student VALUES(8, '不想毕业', '200003', 18, 1, '2000-09-01', 2);
那如果想要查找出表中没有考试的同学的名字和信息,应该怎么搞呢
这就要使用到外连接,这里外连接的两个表就是student表和score表
以左连接为例,就以student表作为基准表,student的所有信息全部显示,score表没有对应的记录,列中的数据就是null
这里进行左连接查询后,就能得到一个表,score是右表,里面的信息匹配不上,就用Null填充
sql
SELECT * from student s left JOIN score sc on s.id = sc.student_id

接着再加个where筛选下,就可以把这个同学的成绩找出来
sql
SELECT * from student s left JOIN score sc on s.id = sc.student_id
WHERE sc.score is NULL

再比如查询没有学生的班,前面创建了一个python班,里面没有放同学
右连接查询示例如下:
直接查就是这样的
sql
SELECT * from student s right join class c on s.class_id = c.id

接着在原来sql语句的基础上加上筛选null和精简查询,就完成了班级的查询
sql
SELECT c.name as "没有学生的班级"
from student s
right join class c on s.class_id = c.id
where s.id is null
这里就是以class为基准表

外连接,总结起来,就是把基准表放在左连接的左边,或者右连接的右边
4,自连接
例如这样一张表,我们想筛选出chinese > math的同学,就很简单,因为这是列跟列之间的比较

这里的score表,想筛选出MySQL分数高于Java分数的同学,直接筛选恐怕就不行,因为要进行的是行与行之间的比较,MySQL是不支持的

想要筛选,就要用到自连接了
自连接是自己与自己取笛卡尔积,可以把行转化成列 ,在查询的时候可以使用 where 条件对结果进行过滤,或者说实现行与行之间的比较。注:在做表连接时为表起不同的别名
自己对自己取笛卡尔积,这里就会报错(如下图),报错的原因是:表 / 别名不唯一
这是因为数据库无法区分这两个score表

解决这一问题,就要给表加上别名,这样就会得到一个超级超级长的数据集(行数是19 * 19),这样就把行成功的转成了列
sql
SELECT * from score s1,score s2

接着查询下course表,Java的Java的id是1,MySQL的id是3
sql
SELECT * from course

接着就是确定条件,过滤出无用的数据集
sql
SELECT * from score s1,score s2
WHERE s1.student_id = s2.student_id AND s1.course_id = 1
AND s2.course_id = 2

最后后面加个比较大小的and语句,再精简下查询,即可完成筛选(如下)
sql
SELECT s1.id "学生编号",s1.score "Java分数",s2.score "数据库分数"
from score s1,score s2
WHERE s1.student_id = s2.student_id
AND s1.course_id = 1
AND s2.course_id = 3
AND s2.score > s1.score

现在再升级一下需求,动态输入要比较的课程名,进行比较(让课程名跟在course自动匹配)
那这时候就要用到score表和course表
代码如下:s1代表Java,s2代表数据库,这里要搞两个course表,用c1来过滤出score中的Java部分,用c2过滤出score中的MySQL部分,接着进行比较即可
sql
SELECT * from score s1,score s2,course c1,course c2
WHERE s1.student_id = s2.student_id
and c1.name = 'Java'
and c1.id = s1.course_id
and c2.name = 'MySQL'
and c2.id = s2.course_id
and s2.score > s1.score

再进行精简查询:
sql
SELECT s1.id '学生编号',s1.score 'Java分数',s2.score 'MySQL分数' from score s1,score s2,course c1,course c2
WHERE s1.student_id = s2.student_id
and c1.name = 'Java'
and c1.id = s1.course_id
and c2.name = 'MySQL'
and c2.id = s2.course_id
and s2.score > s1.score

结语💕💕
这部分非常基础,是mysql查询的一个核心部分,用到最多的就是基础联合查询,这部分知识并不难,铁汁们多练习,应该很快就会掌握🐵
以上就是今天的所有内容啦~完结撒花~🥳🎉🎉

