多表关系
在项目开发进行数据库结构设计时,根据业务需求和业务模块之间的关系进行分享并设计表的结构,而由于业务之间是相互关联的,所以各个表结构之间也存在着各种联系,主要分为 一对多(多对一)、多对多、一对一 这三种
一对多(多对一)
例如部门与员工的关系:一个部门可以对应多个员工,而一个员工则对应一个部门,这种可以通过在多的一方建立外键,指向一的一方的主键
多对多
例如学生与课程的关系:一个学生可以选修多门课程,而一个课程可以被多名学生选修,这种可以通过建立第三张中间表,该表至少包含两个外键,分别关联两方主键
创建学生表并插入数据:
1 | -- 学生表 |
创建课程表并插入数据:
1 | create table course( |
上述两张表没有关联,可以通过中间表进行关联:
1 | create table student_course( |
一对一
例如用户与用户详情的关系。一对一的关系多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段则放在另一张表中,用以提升操作效率
这种关系跟一对多(多对一)的处理类似,区别在于该外键是唯一(UNIQUE)的
创建用户基本信息表并插入数据:
1 | CREATE TABLE tb_user ( |
创建用户详情表并插入数据:
1 | CREATE TABLE tb_user_edu( |
可以看出对 userid 使用了唯一 UNIQUE
多表查询
从多张表中查询数据。按照 SELECT * FROM A表,B表 来进行查询会造成数据冗余,即笛卡尔积。很明显,这种数据并不是我们所需要的,所以在进行多表查询时我们需要消除无效的笛卡尔积
笛卡尔积:两个集合的所有组合情况,即A集合有2条数据,B集合有3条数据,那么总共会形成6条数据
多表查询主要分为 连接查询 和 子查询
- 连接查询分为 内连接、外连接、自连接
- 子查询,又被称为嵌套查询,分为 标量子查询、列子查询、行子查询、表子查询
连接查询
内连接
内连接所查询的是两张表所交集的部分,分为隐式内连接和显式内连接
1 | -- 隐式内连接 |
查询员工所对应的部门:
1 | -- 隐式内连接 |
外连接
外连接分为左外连接和右外连接:
1 | -- 左外连接:表1数据 + 表1和表2交集 |
练习:
1 | -- emp表的所有数据和其对应的部门信息 |
自连接
自连接可以是内连接,也可以是外连接
1 | SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件; |
练习:
1 | -- 员工的所属领导 |
联合查询
将多次查询的结果合并起来形成一个新的结果集:
1 | SELECT 字段列表 FROM 表A |
练习:
1 | -- 薪资低于10000的员工和年龄大于30岁的员工 |
多张表的列数必须保持一致,字段类型也必须要保持一致
UNION ALL 会将全部查询返回的数据直接合并在一起,而 UNION 会对合并之后的数据去重
子查询
子查询语法:
1 | SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2) |
该外部语句除了可以是查询外,还能是增删改其中一种
标量子查询
所返回的结果是单个值(字符串、数字、日期等),是最简单的形式
常用的操作符有:=、<>、>、>=、<、<=
练习:
1 | -- 杨逍之后入职的员工信息 |
列子查询
子查询所返回的结果是一列(也可以是多行),这种子查询称为列子查询
常见的操作符如下图所示:
练习:
1 | -- 查询销售部和市场部的所有员工信息 |
行子查询
子查询所返回的结果是一行(也可以是多列),这种子查询称为行子查询
常用的操作符有:=、<>、IN、NOT IN
练习:
1 | -- 查询与'张无忌'的薪资及直属领导相同的员工信息 |
行子查询
子查询所返回的结果是多行多列,这种子查询称为表子查询
常用的操作符有:IN
练习:
1 | -- 查询与'杨逍'薪资和工作相同的员工信息 |
案例练习
建表
上述练习没有删除 emp表 和 dept表 的数据,但是基本操作相同,除了数据
现在重新创建 dept表,并插入相应数据:
1 | CREATE TABLE dept ( id INT PRIMARY KEY auto_increment, NAME VARCHAR ( 10 ) ); |
创建 emp表,并插入数据:
1 | CREATE TABLE emp ( |
创建 salgrade表,并插入相应数据:
1 | CREATE TABLE salgrade ( grade INT, losal INT, hisal INT ) COMMENT '薪资等级表'; |
练习
1、查询员工的姓名、年龄、职位、部门信息(隐式内连接)
1
SELECT e.name,e.age,e.job,d.name as '部门' FROM emp e, dept d WHERE e.dept_id = d.id;
2、查询年龄小于30岁的员工姓名、年龄、职位、部门信息(显示式内连接)
1
SELECT e.name,e.age,e.job,d.name as '部门' FROM emp e INNER JOIN dept d ON e.dept_id = d.id && e.age < 30;
3、查询所有员工的部门ID、部门名称
1
SELECT DISTINCT d.id,d.name as '部门' FROM emp e , dept d WHERE e.dept_id = d.id;
4、查询所有年龄大于40岁的员工及其所属部门名称,如果没有也需要展示出来
1
SELECT e.*,d.name as '部门' FROM emp e LEFT JOIN dept d ON e.dept_id = d.id WHERE e.age > 40;
5、查询所有员工的工资等级
1
SELECT e.*,s.grade AS '薪资等级' FROM emp e,salgrade s WHERE e.salary BETWEEN s.losal AND s.hisal;
6、查询’研发部’所有员工的信息及工资等级
1
SELECT e.*,s.grade FROM (SELECT * FROM emp WHERE emp.dept_id = (SELECT id FROM dept WHERE name = '研发部')) e,salgrade s WHERE e.salary BETWEEN s.losal AND s.hisal;
7、查询’研发部’的平均工资
1
SELECT d.name as '部门',AVG(e.salary) AS '平均工资' FROM emp e,dept d WHERE e.dept_id = d.id and d.name = '研发部';
8、查询工资比’灭绝’高的员工信息
1
SELECT * FROM emp WHERE salary > (SELECT salary FROM emp WHERE emp.name = '灭绝');
9、查询比平均薪资高的员工信息
1
SELECT * FROM emp WHERE salary > (SELECT AVG(emp.salary) FROM emp);
10、查询低于本部门平均薪资的员工信息
1
2SELECT AVG(e.salary) FROM emp e WHERE e.dept_id = 3;
SELECT * FROM emp e2 WHERE salary < (SELECT AVG(e.salary) FROM emp e WHERE e.dept_id = e2.dept_id);11、查询所有的部门信息,并统计部门的人数
1
2SELECT COUNT(*) FROM emp WHERE dept_id = 1;
SELECT d.id,d.name,(SELECT COUNT(*) FROM emp e WHERE e.dept_id = d.id) AS '人数' FROM dept d;12、查询所有学生的选课情况,展示学生名称、学号、课程名称
1
2
3-- 所涉及表:student、course、student_course
-- 连接条件:student.id = student_course.studentid、course.id = student_course.courseid
SELECT s.name,s.no,c.name FROM student s,course c,student_course sc WHERE s.id = sc.studentid AND c.id = sc.courseid;