Tlias智能学习系统
员工管理:
多表关系:
1.一对多
场景:部门与员工的关系(一个部门下有多个员工)
体现:需要在多的一方添加字段,关联一的一方的主键
多表问题分析:
现象:部门数据可以直接删除,然而还有部分员工归属于该部门下,此时就出现了数据的不完整、不一致问题。
原因:目前上述的两张表,在数据库层面,并未建立关联,所以是无法保证数据的一致性和完整性。
解决办法:外键约束。
外键约束的语法:
-- 创建表时指定
create table 表名(字段名 数据类型,...[constraint] [外键名称] foreign key (外键字段名) references 主表 (主表列名)
);
-- 建完表后,添加外键
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
外键约束:
物理外键:使用foreign key 定义外键关联另一张表。缺点:1.影响增、删、改的效率(需要检测外键关系)。2.仅用于单节点数据库,不适用于分布式、集群场景。3.容易引发数据库的死锁问题,消耗性能。
逻辑外键:在业务层逻辑中,解决外键关联。优点:通过逻辑外键,就可以很方便的解决上述问题。
2.多对多
案例:学生 与 课程的关系
关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
3.一对一
案例:用户 与 身份证信息 的关系。
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他字段放在另一张表中,以提升操作效率。
实现:在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)。
案例:根据页面原型以及需求文档分析并设计表结构
1.需求:请根据资料中提供的页面原型,设计 员工模块 涉及到的表结构。
2.步骤:
阅读页面原型以及需求文档,分析各个模块涉及到表结构,以及表结构之间的关系。
根据页面原型以及需求文档,分析各个表结构中具体的字段以及约束。
多表查询:
1.多表查询:指从多张表中查询数据。
2.笛卡尔积:指在数学中,两个集合(A集合 和 B集合)的所有组合情况。(在多表查询时,需要消除无效的笛卡尔积)
3.连接查询:
内连接:相当于查询A、B交集部分数据。
具体实现:
-- ============================= 内连接 ==========================
-- A. 查询所有员工的ID, 姓名 , 及所属的部门名称 (隐式、显式内连接实现)
-- 隐式
select emp.id,emp.name,dept.name from dept , emp where emp.dept_id=dept.id;
-- 显式(inner可以省略)
select emp.id,emp.name,dept.name from dept inner join emp on emp.dept_id=dept.id;
select emp.id,emp.name,dept.name from dept join emp on emp.dept_id=dept.id;
-- B. 查询 性别为男, 且工资 高于8000 的员工的ID, 姓名, 及所属的部门名称 (隐式、显式内连接实现)
-- 隐式
select emp.id,emp.name,dept.name from dept , emp where emp.dept_id=dept.id and emp.gender=1 and emp.salary>8000;
-- 显式(inner可以省略)
select emp.id,emp.name,dept.name from dept join emp on emp.dept_id=dept.id where emp.gender=1 and emp.salary>8000;
-- 为表起别名
select e.id,e.name,d.name from dept d join emp e on e.dept_id=d.id where e.gender=1 and e.salary>8000;
外连接:左外连接:查询左表所有数据(包括两张表交集部分数据)右外连接:查询右表所有数据(包括两张表交集部分数据)
具体实现:
-- =============================== 外连接 ============================
-- A. 查询员工表 所有 员工的姓名, 和对应的部门名称 (左外连接)
select e.name , d.name from emp e left outer join dept d on e.dept_id = d.id;
-- B. 查询部门表 所有 部门的名称, 和对应的员工名称 (右外连接)
select d.name , e.name from emp e right join dept d on e.dept_id = d.id;
-- C. 查询工资 高于8000 的 所有员工的姓名, 和对应的部门名称 (左外连接)
select e.name,d.name from emp e left join dept d on d.id = e.dept_id where e.salary>8000;
提示:对于外连接,常用的是左外连接,因为右外连接的SQL也可以改造成为左外连接(两张表调换个顺序)。
4.子查询:
介绍:SQL语句中嵌套select语句,称为嵌套查询,又称为子查询。
形式:select * from t1 where column1 = (select column1 from t2 ...);
说明:子查询外部的语句可以是insert、update、delete、select的任何一个,最常见的是select。
分类:
1.标量子查询:子查询返回的结果为单个值。
2.列子查询:子查询返回的结果为一列。
3.行子查询:子查询返回的结果为一行。
4.表子查询:子查询返回的结果为多行多列。
(提示:子查询的要领是,先对需求做拆分,明确具体的步骤,然后再逐步编写SQL语句。)
-- ========================= 子查询 ================================
-- 标量子查询
-- A. 查询 最早入职 的员工信息
-- a. 获取到最早入职时间select min(entry_date) from emp;
-- b.查询最早入职的员工信息select * from emp where entry_date='2000-01-01';
select * from emp where entry_date=(select min(entry_date) from emp);
-- B. 查询在 "阮小五" 入职之后入职的员工信息
-- a. 查询“阮小五”入职的时间select entry_date from emp where name = '阮小五';
-- b. 查询在该时间之后入职的员工信息select * from emp where entry_date > '2015-01-01';
select * from emp where entry_date > (select entry_date from emp where name = '阮小五');
-- 列子查询
-- A. 查询 "教研部" 和 "咨询部" 的所有员工信息
-- a.查询“教研部”和“咨询部”的部门IDselect id from dept where name='教研部' or name='咨询部';
-- b.查询指定部门ID的员工信息select * from emp where dept_id in (2,3);
select * from emp where dept_id in (select id from dept where name='教研部' or name='咨询部');
-- 行子查询
-- A. 查询与 "李忠" 的薪资 及 职位都相同的员工信息 ;
-- a. 查询 "李忠" 的薪资 及 职位select salary,job from emp where name = '李忠';
-- b. 查询指定薪资和职位的员工信息select * from emp where job = 5 and salary = 5000;
select * from emp where job = (select job from emp where name = '李忠') and salary = (select salary from emp where name = '李忠');
-- 优化:select * from emp where (salary,job) = (select salary,job from emp where name = '李忠');
-- 表子查询
-- A. 获取每个部门中薪资最高的员工信息
-- a. 获取每个部门的最高薪资select dept_id,max(salary) from emp group by dept_id;
-- b. 查询每个部门中薪资最高的员工信息select * from emp e,(select dept_id,max(salary) max_salary from emp group by dept_id) awhere e.salary = a.max_salary and e.dept_id = a.dept_id;
案例:
-- 需求:
-- 1. 查询 "教研部" 性别为 男,且在 "2011-05-01" 之后入职的员工信息 。select * from emp where entry_date>'2011-05-01'and id in (select id from emp where gender = 1 and dept_id = ( select id from dept where name = '教研部'));
-- 答案:select e.* from emp as e , dept as d where e.dept_id = d.id and d.name = '教研部' and e.gender = 1 and e.entry_date > '2011-05-01';
-- 2. 查询工资 低于公司平均工资的 且 性别为男 的员工信息 。select avg(salary) from emp;select * from emp where salary<(select avg(salary) from emp) and gender = 1;
-- 3. 查询部门人数超过 10 人的部门名称 。
-- 答案:select d.name,count(*) from emp e,dept d where e.dept_id = d.id group by d.name having count(*)>10;
-- 4. 查询在 "2010-05-01" 后入职,且薪资高于 10000 的 "教研部" 员工信息,并根据薪资倒序排序。select * from emp where entry_date>'2010-05-01' and salary>10000and dept_id=(select id from dept where name='教研部') order by salary desc ;
-- 答案:select * from emp e , dept d where e.dept_id = d.id and e.entry_date > '2010-05-01'and e.salary > 10000 and d.name = '教研部' order by e.salary desc;
-- 5. 查询工资 低于本部门平均工资的员工信息select dept_id,avg(salary) from emp group by dept_id;
-- 答案;
-- 5.1 查询每个部门的平均工资select dept_id, avg(salary) avg_sal from emp group by dept_id;
-- 5.2 查询工资 低于本部门平均工资 的员工信息 。select e.* from emp e , (select dept_id, avg(salary) avg_sal from emp group by dept_id) as awhere e.dept_id = a.dept_id and e.salary < a.avg_sal;