MySQL 表的增删改查(进阶篇②)· 联合查询 内连接 外连接 · 自连接 · 子查询 exists · 合并查询 union

接进阶篇①,我们继续学习。

一、联合查询

实际开发中往往数据来自不同的表,所以需要多表联合查询。

多表查询是对多张表的数据取笛卡尔积。

我们先初始化测试数据:

insert into classes(name, `desc`) values
('计算机系2019级1班', '学习了计算机原理、C和Java语言、数据结构和算法'),
('中文系2019级3班','学习了中国传统文学'),
('自动化2019级5班','学习了机械自动化');

insert into student(sn, name, qq_mail, classes_id) values
('09982','黑旋风李逵','xuanfeng@qq.com',1),
('00835','菩提老祖',null,1),
('00391','白素贞',null,1),
('00031','许仙','xuxian@qq.com',1),
('00054','不想毕业',null,1),
('51234','好好说话','say@qq.com',2),
('83223','tellme',null,2),
('09527','老外学中文','foreigner@qq.com',2);
insert into course(name) values
('Java'),('中国传统文化'),('计算机原理'),('语文'),('高阶数学'),('英文');

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),
-- tellme
(80, 7, 2),(92, 7, 6);

1.1 内连接

语法:

select 字段 from 表1 别名1 [inner] join 表2 别名2 on 连接条件 and 其它条件;
select 字段 from 表1 别名1,表2 别名2 where 连接条件 and 其它条件;

案例一: 查询 “许仙” 同学的成绩.

-- 写法一
select student.name,score.score from student,score where student.id=score.student_id and student.name='许仙';

--写法二
select student.name,score.score from student inner join score on student.id=score.student_id and student.name='许仙';

运行结果:

解析:

首先,我们需要判断出会涉及到哪些表,学生表和成绩表

其次,将两个表先进行笛卡尔积操作(会出现很多数据);

select * from student,score;

接着,给 sql 语句添加连接条件,将无意义的数据过滤掉;

select * from student,score where student.id=score.student_id;

然后,逐渐添加题目中的其它条件(学生名字),一步步缩小数据范围;

select * from student,score where student.id=score.student_id and student.name='许仙';

最后再精简查询的列,就是最后的答案。

select student.name,score.score from student,score where student.id=score.student_id and student.name='许仙';

案例二: 查询所有同学的总成绩,以及同学的个人信息

-- 成绩表对学生表是 n 对 1 关系,查询总成绩需要根据成绩表的同学 id 来进行分组的
select stu.sn,stu.name,stu.qq_mail,sum(sco.score) from student stu,score sco where stu.id=sco.student_id group by sco.student_id;

执行结果:


案例三: 查询所有同学的成绩,以及同学的个人信息。

-- 学生表、成绩表、课程表 3 张表关联查询
select stu.sn,stu.name,stu.qq_mail,sco.score,sco.course_id,cou.name 
	from student stu,score sco,course cou 
	where stu.id=sco.student_id and cou.id=sco.course_id;

运行结果:


1.2 外连接

外连接分为左外连接和右外连接。
如果联合查询,左侧的表完全显示我们称为左外连接;右侧的表完全显示我们称为右外连接。

语法:

-- 左外连接
select 字段名 from 表名1 left join 表名2 on 连接条件;
-- 右外连接
select 字段 from 表名1 right join 表名2 on 连接条件;

案例: 查询所有同学的成绩,以及同学的个人信息。如果该同学没有成绩,也需要显示。

select stu.id,stu.sn,stu.name,stu.qq_mail,sco.score,sco.course_id,cou.name 
from student stu 
	left join score sco on stu.id=sco.student_id 
	left join course cou on sco.course_id=cou.id 
order by stu.id;

运行结果:


1.3 内连接和左右外连接的区别

假设两个表的每一条数据都是一一对应的,此时外连接和内连接是等价的。
如果有时候可能存在一些数据,没有对应关系,内外连接就有差别了。

例如:

内连接: 两个表内连接,有个没有成绩的同学没有显示出来。

左外连接: 一共 21 条记录,“老外学中文” 同学可以显示出来。

右外连接: 一共 21 条记录,“老外学中文” 同学可以显示出来。

观察案例可以发现,内连接的结果只包含在两张表都有体现的数据;
左外连接,左侧的表的数据是全的;
右外连接,右侧的表的数据是全的。



二、自连接

自连接是指在同一张表连接自身进行查询。

案例: 查询所有 “计算机原理” 成绩比 “Java” 成绩高的成绩信息。

select s1.student_id, s1.score as java, s2.score as '计算机原理' 
from score s1, score s2 
where s1.student_id = s2.student_id 
	and s1.course_id=1 
	and s2.course_id=3 
	and s1.score<s2.score;

-- join on 语句的写法来进行自连接查询
select s1.student_id,
	s1.score as java,
	s2.score as '计算机原理' 
from score s1 join score s2 
on s1.student_id=s2.student_id 
	and s1.score < s2.score 
	and s1.course_id=1 
	and s2.course_id=3;

运行结果:

以上查询只显示了成绩信息,要显示学生以及成绩信息,并且在一条语句显示:

SELECT
	stu.*,
	s1.score Java,
	s2.score 计算机原理
FROM
	score s1
	JOIN score s2 ON s1.student_id = s2.student_id
	JOIN student stu ON s1.student_id = stu.id
	JOIN course c1 ON s1.course_id = c1.id
	JOIN course c2 ON s2.course_id = c2.id
	AND s1.score < s2.score
	AND c1.NAME = 'Java'
	AND c2.NAME = '计算机原理';

运行结果:



三、子查询

子查询是指嵌入在其它 sql 语句中的 select 语句,也叫嵌套查询。

3.1 单行子查询

返回一行记录的子查询

查询与 “不想毕业” 同学的同班同学

select * from student where classes_id=(
	select classes_id from student where name='不想毕业');


3.2 多行子查询

返回多行记录的子查询

使用 in 范围匹配多行

查询 “语文” 或者 “英文” 课程的成绩信息

select * from score where course_id in (
	select id from course where name='语文' or name='英文');

执行结果:

另一种写法 exists

也可以完成多行子查询

select * from score 
where exists(
	select score,course_id from course 
	where(name='英文' or name='语文') 
	and course.id=score.course_id
);

执行过程:

先执行外层查询,就会得到很多行记录。

每获取到外层查询的一行,就都会执行一次子查询的 SQL。

接下来,如果子查询的结果集合为空,那么外层查询的这一行记录,就被忽略。
如果子查询的结果集合非空,那么外层查询的这一行记录就会被记录。

exists 效果就是检测,先获取数据,在把数据一条一条的执行子查询 SQL,筛选掉不符合子查询的数据。


两种写法的区别

第一种基于 in 的写法,速度快,但是如果子查询的结果集合很大,内存放不下,就凉了。

第二种,基于 exists 的写法,速度慢,但是和内存的关系不大,哪怕子查询很大,也能保证执行出结果。

如果子查询结果集合比较小,优先使用第一种写法。

如果子查询结果集合比较大,并且外层查询的结果数量比较少,优先考虑第二种写法。


3.3 在 from 子句中使用子查询

子查询语句出现在 from 子句中。
这里要用到数据查询的技巧,把一个子查询当成一个临时表来使用。

查询所有比 “中文系2019级3班” 平均分高的成绩信息:

select avg(sco.score) score 
from score sco 
	join student stu on sco.student_id=stu.id 
	join classes cls on stu.classes_id=cls.id 
where cls.name='中文系2019级3班';

查询成绩表中,比以上临时表平均分高的成绩:

select * from score sco,(
	select avg(sco.score) score 
	from score sco 
		join student stu on sco.student_id=stu.id 
		join classes cls on stu.classes_id=cls.id 
	where cls.name='中文系2019级3班'
)tmp where sco.score > tmp.score;


四、合并查询

在实际应用中,为了合并多个 select 的执行结果,可以使用集合操作符 union,union all。
使用 union,union all 时,前后查询的结果集,字段需要保持一致。

union

该操作符用于取得两个结果集的并集。使用该操作符的时候,会自动去掉结果集中重复的行。

查询 id 小于 3,或者名字为 ‘英文’ 的课程

select * from course where id<3 
union 
select * from course where name='英文';

执行结果:

其实使用 or 来实现更加简单。

select * from course where id<3 or name='英文';

union all

该操作符用于取得两个结果集的并集。
当使用此操作符的时候,不会去掉结果集中的重复行。

查询 id 小于 3,或者名字为 ‘Java’ 的课程

select * from course where id<3 union all select * from course where name='Java';

运行结果:有重复行。

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在MySQL中进行递归查询子节点,可以使用以下方法: 1. 使用递归CTE(Common Table Expressions): 在MySQL 8.0版本及以上,可以使用递归CTE来实现递归查询。递归CTE允许在查询中递归引用同一,并在每次迭代中执行递归步骤。例如: ```sql WITH RECURSIVE sub_tree AS ( SELECT id, parent_id, name FROM t_files WHERE id = <初始节点ID> UNION ALL SELECT f.id, f.parent_id, f.name FROM t_files f JOIN sub_tree st ON f.parent_id = st.id ) SELECT * FROM sub_tree; ``` 上述查询中,使用了一个递归CTE子查询`sub_tree`,它首先选择初始节点,然后通过递归连接`t_files`自身,直到找到所有子节点。 2. 使用存储过程: 如果您的MySQL版本不支持递归CTE,可以通过编写存储过程来实现递归查询。存储过程可以使用循环和条件语句来逐级查询子节点。以下是一个示例存储过程: ```sql DELIMITER // CREATE PROCEDURE get_child_nodes(IN initial_id INT) BEGIN DECLARE done INT DEFAULT FALSE; DECLARE current_id INT; DECLARE child_id INT; DECLARE child_name VARCHAR(255); DECLARE cur CURSOR FOR SELECT id, name FROM t_files WHERE parent_id = initial_id; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; CREATE TEMPORARY TABLE IF NOT EXISTS temp_results (id INT, name VARCHAR(255)); OPEN cur; read_loop: LOOP FETCH cur INTO child_id, child_name; IF done THEN LEAVE read_loop; END IF; INSERT INTO temp_results VALUES (child_id, child_name); CALL get_child_nodes(child_id); END LOOP; CLOSE cur; SELECT * FROM temp_results; DROP TEMPORARY TABLE IF EXISTS temp_results; END // DELIMITER ; ``` 在上述存储过程中,通过使用游标和递归调用存储过程来获取所有子节点,并将结果存储在一个临时中。 以上是两种常用的方法来在MySQL中进行递归查询子节点。您可以根据您的具体需求选择适合您的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值