--- title: "13-多表连接查询" created: 2025-11-25 tags: - 项目筑基 --- # 多表连接查询 ### 多表查询 从多张表查询数据 分类:连接查询和子查询 #### 连接查询 联接条件最常见的写法是将两个表的公共列的名字用比较运算符连接起来 (注意:在列名前必须加上表名限制,以限定列的所属)。 公共列的两种常见形态: 1.公共列在两个表中均为主键。 2.公共列是一个表的主键,但在另一个表中是外键。 例如: ON 学院.学院编号 = 课程.学院编号 ##### 内连接 分为等值连接(=)、非等值连接(>,>=,<,<=等连接关系不是等值的)、自连接 (自己连自己)、自然连接(去重复字段) 还分为隐式内连接(where)和显式内连接(inner join) 一般都不用where连接 因为它表示不清晰 到底是连接还是筛选? 因此也就发展出了join 连接就是连接 连接完后还可以进行where操作 **等值连接 (条件是等价关系)** 使用“=”将有相同字段的数据结合在一起 ![[image-de4531fa.png]] 这是使用where语句的隐式连接 可见结果是把stu表放在左边 sc表放在右边 筛选出相同字段的数据并结合加以显示 使用INNER JOIN语句 可见结果是一模一样的 ![[image-42512009.png]] **非等值连接** ![[image-1c14c743.png]] 非等值查询也就是说连接条件中 ON里面没用“=”的查询 自连接 ![[image-32cd1ab0.png]] 对于这样一张表 存储了所有员工 员工信息后面又对应了其领导的编号 那么想查询员工名对应的领导名怎么办——自己和自己连接 把一张表看成两张表 看成emp a 员工表 emp b 领导表 ```sql Select a.ename as '员工名',b.ename as '领导名' from emp a join emp b on a.mgr = b.empno ``` 用别名 将表当成两个表 将mgr字段与empno字段进行连接 **自然连接** ![[image-c7e7ffb8.png]] 由上图等值查询所示 它会把重复的列显示两次 怎么把那个多出来的sno去掉? 使用DISTINCT??? 不 DISTINCT是用于删除重复行(记录) 重复列怎么删? 这就要用到自然连接 但sql server没用这个关键词 在其他数据库软件中使用 natural join就行 在这里就只能使用笨方法——自己输入想显示的 ![[image-0db8dd34.png]] 可见 重复的sno不再予以显示 三个表也可以做到(多个表以此类推) ![[image-ac571103.png]] ![[image-414f670d.png]] ##### 外连接 令A为左表 B为右表 (案例见别名的妙用) 左外连接 LEFT (OUTER) JOIN:相当于查询A表所有数据和交集部分数据 右外连接 RIGHT (OUTER) JOIN:相当于查询B表所有数据和交集部分数据 全连接FULL(OUTER) JOIN:把左右外连接会筛掉的行都保留下来 不满足的记录用NULL填充 做几组对比 ![[image-c225a446.png]] `SELECT * FROM stu`: ![[image-0160f6dd.png]] `SELECT * FROM sc`: ![[image-ac9a0845.png]] 将stu作为左表(stu join sc join左边的是stu 即为左表) ![[image-8d1c1752.png]] 将sc作为左表: ![[image-6fae6a91.png]] 可以观察到 因为是select stu.\*,sc.\* 所以两个结果都为 ![[image-66b1af6a.png]] 这种格式(stu的放在左边 右边接上sc) 不同在于 当stu作为左表时 结果是先把stu中符合条件的数据都选了出来 形成了“新表”的格式 然后再从sc表选择合适的字段加以显示 不满足的部分填充NULL 图二是先筛选了sc中符合条件的内容 再在此结构上筛选stu符合的内容(由于stu的数据都满足条件 所以本例中未出现NULL) 这样一来,“用于显示符合条件的数据行以及左边表中不符合条件的数据行,此时右边数据行会以NULL来显示”这句话就能理解了 当然 右外连接就跟它相反就行了(先把join右边的表查询定格式 再补join左边的表 满足条件插入 不满足填NULL)——“用于显示符合条件的数据行以及右边表中不符合条件的数据行。此时左边数据行会以NULL来显示 ” 了解其运行原理 那么再来对比一下 其实左连接和右连接是可以达成相同效果的 ![[image-a7a4194c.png]] 就是把顺序交换一下而已 \*Right:把join关键字右边的表看成主表 主要将这张表的数据全部查询出来 只是捎带着关联查询左边的表(捎带 有就显示 没有就NULL) left同理 在外连接中 两张表产生了主次关系 还有一个FULL JOIN ![[image-9aa83e28.png]] (1) ![[image-78c3d9b6.png]] (2) ![[image-eed32b99.png]] (3) ![[image-2075c92c.png]] 这个例子不太好 其实它的功能就是把所有的被筛掉的记录行都显示出来 不满足用NULL填 “显示符合条件的数据行以及左边表和右边表中不符合条件的数据行。此时缺乏数据的数据行会以NULL来显示”(全连接是将两个表都作为主表) 在实际用法中 肯定不是使用\* 用\*没什么实际意义 记住外连接作用是把一个表当主要的表 然后附带显示另一个表的数据 理解后再加上一些筛选字段就有实际用途了 ##### 交叉连接 就是笛卡尔积 (其实直接select \* from 表1,表2 的结果也是笛卡尔积) CROSS JOIN(无条件) 无条件就是指 不需要ON什么什么 ![[image-d42f0058.png]] 结果图上有144条记录 不截屏了 三表也同样适用 ![[image-f2ea9583.png]] 加括号是我的理解(不需要括号) 可能内部先进行的是将左边俩先结合 得到的结果再与第三个表结合 ##### 小总结 Inner join 显示的内容最少 它只显示同时符合条件的数据行 Left/right join 显示的较多些 显示的行数主要由第一个表决定 FULL join 又要比左右连接更多些 CROSS join 是完全不筛选 把各种情况做一个组合 显示内容非常多 (INNER、OUTER完全可以省略) ##### 连接的逻辑思路 新建数据库关系图 根据绘制各表间的关系图来进行一个指导 知道表和表之间通过哪个地方相连接 思路清晰的话 连接是不难的 用表1的某字段 去遍历表2 进行一一匹配 符合条件的就加入进去 ##### 连接后可以做其他操作 将各表连接后 是可以与WHERE、GROUP BY、ORDER BY……连用的 查询教师欧阳淑芳所上的所有课堂,要求按照开课年份和开课学期的升序,显示课堂名称、开课年份、开课学期。 ```sql SELECT 课堂.课堂名称, 课堂.开课年份, 课堂.开课学期 FROM 教师 INNER JOIN 课堂 ON 教师.教师编号 = 课堂.教师编号 WHERE 教师.姓名 = '欧阳淑芳' ORDER BY 课堂.开课年份, 课堂.开课学期 ``` 查询王志强老师讲授的所有学生的名单,显示学号、姓名、专业班级。 ```sql SELECT 学生.学号, 学生.姓名, 成绩, 专业班级 FROM 教师 INNER JOIN 课堂 ON 教师.教师编号 = 课堂.教师编号 INNER JOIN 选课成绩 ON 课堂.课堂编号 = 选课成绩.课堂编号 INNER JOIN 学生 ON 选课成绩.学号 = 学生.学号 WHERE 教师.姓名 = '王志强' ``` 统计计算机学院每一位教师本学期的教学工作量(指学时数),要求显示教师编号和学时数,并按学时数的降序排列。 ```sql SELECT 教师.教师编号, SUM(课程.学时数) AS 学时数 FROM 教师 INNER JOIN 课堂 ON 教师.教师编号 = 课堂.教师编号 INNER JOIN 课程 ON 课堂.课程编号 = 课程.课程编号 INNER JOIN 学院 ON 教师.学院编号 = 学院.学院编号 WHERE 课堂.开课年份 = '2017-2018' AND 课堂.开课学期 = '一' and 学院名称='计算机学院' GROUP BY 教师.教师编号 ORDER BY 学时数 DESC ``` --- ⬅️ [[12-排序与语句执行顺序|12-排序与语句执行顺序]] 🏠 [[00-数据库|00-数据库]] ➡️ [[14-子查询与结果集归并|14-子查询与结果集归并]]