一.介绍
1.实际开发中,一个项目通常需要很多张表才能完成,例如:一个商城项目就需要分类表,商品表,订单表等多张表,且这些表的数据之间存在一定的关系
2.多表关系:一对一,一对多/多对一,多对多
(1)一对一关系
一个学生只有一张身份证,一张身份证只能对应一个学生;在任一表中添加唯一外键,指向另一方主键,确保一对一关系;一般一对一关系很少见,遇到一对一关系的表最好是合并表。
(2)一对多/多对一关系
一个部门有多个员工,一个员工只能对应一个部门;实现原则:在多的一方建立外键,指向一的一方的主键。
(3)多对多关系
一个学生可以选择多门课程,一个课程可以被多名学生选择;原则:多对多关系实现需要借助第三张中间表,中间表至少包含两个字段,将多对多的关系,拆分成一对多的关系,中间表至少要有两个外键,这两个外键分别指向原来的那两张表的主键。
二.外键约束
3. 操作
方式一:在创建表时设置外键约束
方式二
-- 实现
use mydb1;
-- 创建部门表--主表
create table if not exists dept (
detpno varchar(20) primary key, -- 部门号 主键列
name varchar(20) -- 部门名字
);
-- 创建员工表,从表,并创建外键约束
-- 方式一
create table if not exists emp (
eid varchar(20) primary key, -- 员工编号
ename varchar(20), -- 员工名字
age int, -- 员工年龄
dept_id varchar(20), -- 员工所属部门
constraint emp_fk foreign key(dept_id) references dept(detpno)
);
-- 方式二
create table if not exists emp (
eid varchar(20) primary key, -- 员工编号
ename varchar(20), -- 员工名字
age int, -- 员工年龄
dept_id varchar(20), -- 员工所属部门
);
alter table emp add constraint emp_fk foreign key (dept_id) references dept(detpno);
4.在外键约束下的数据操作
(1)数据插入
(2)删除数据
上述操作中没有使用1004,可以在主表中删除
5.删除外键约束
当一个表中不需要外键约束时,就需要从表中将其删除。外键一旦删除,就会解除主表和从表间的关联关系
6.外键约束--多对多关系
use mydb1;
-- 学生表和课程表(多对多)
-- 1.创建学生表student(左侧主表)
create table if not exists student(
sid int primary key auto_increment,
name varchar(20),
age int,
gender varchar(20)
);
-- 2.创建课程表course(右侧主表)
create table course(
cid int primary key auto_increment,
cidname varchar(20)
);
-- 3.创建中间表student_course/score(从表)
create table score(
sid int,
cid int,
score double
);
-- 4.建立外键约束(2次)
alter table score add foreign key(sid) references student(sid);
alter table score add foreign key(cid) references course(cid);
-- 5.给学生表添加数据
insert into student values(1,'小龙女',18,'女'),(2,'阿紫',19,'女'),(3,'周芷若',20,'男');
-- 6.给课程表添加数据
insert into course values(1,'语文'),(2,'数学'),(3,'英语');
-- 7.给中间表添加数据
insert into score values(1,1,97),(1,2,87),(2,1,89),(2,3,88),(3,2,87),(3,3,95);
三.多表联合查询
2.准备查询步骤
3.交叉连接查询
交叉连接查询返回被连接的两个表所有数据x行的迪卡尔积;笛卡尔积可以理解为一张表的每一行去和另外一张表的任意一行进行匹配;假如A表有m行数据,B表有n行数据,则返回m*n行数据;笛卡尔积会产生很多多余的数据,后期的其他查询可以在该集合的基础上进行条件筛选
4.内连接查询
use mydb1;
-- 查询每个部门的员工
-- 隐式内连接
select * from dept3,emp3 where deptno = dept_id;
select * from dept3,emp3 where dept3.deptno = emp3.dept_id;
-- 显式内连接
select * from dept3 inner join emp3 on deptno = dept_id;
-- 查询研发部门的所属员工
select * from dept3 inner join emp3 on dept3.deptno = emp3.dept_id and name = '研发部';
-- 查询研发部和销售部的所属员工
select * from dept3 inner join emp3 on dept3.deptno = emp3.dept_id and (name = '研发部' or name = '销售部');
select * from dept3 inner join emp3 on dept3.deptno = emp3.dept_id and name in ('研发部','销售部');
-- 查询每个部门的员工数,并升序排序
select dept3.name,dept3.deptno,count(*) from dept3 inner join emp3 on dept3.deptno = emp3.dept_id group by dept3.deptno;
-- 查询人数大于等于3的部门,并按照人数降序排序
select
dept3.name,
dept3.deptno,
count(*) as total_cnt
from dept3
inner join emp3 on dept3.deptno = emp3.dept_id
group by
dept3.deptno,dept3.name
having
total_cnt >=3
order by
total_cnt desc;
5.外连接查询
外连接分为左外连接(left outer join),右外连接(right outer join),满外连接(full outer join)。注意:oracle里面有full join,可是在mysql对full join 支持的不好,我们可以使用union来达到目的
-- 外连接查询
-- 查询哪些部门有员工,哪些部门没有员工
use mydb1;
select * from dept3 left outer join emp3 on deptno = dept_id;
select * from dept3 left join emp3 on deptno = dept_id;
-- 多表(左外)
select * from A
left join B on 条件1
left join C on 条件2
left join D on 条件3
-- 查询哪些员工有对应的部门,哪些没有
select * from dept3 right outer join emp3 on deptno = dept_id;
select * from dept3 right join emp3 on deptno = dept_id;
-- 多表(右外)
select * from A
right join B on 条件1
right join C on 条件2
right join D on 条件3
-- 实现满外连接
-- 使用union关键字实现左外连接和右外连接的并集
-- select * from dept3 full join emp3 on deptno = dept_id;不能执行
-- union 是将两个查询结果上下拼接,去重
select * from dept3 left join emp3 on deptno = dept_id;
union
select * from dept3 right join emp3 on deptno = dept_id;
-- union all 是将两个查询结果上下拼接,不会去重
select * from dept3 left join emp3 on deptno = dept_id;
union all
select * from dept3 right join emp3 on deptno = dept_id;
6.子查询
(1)子查询就是指的是在一个完整的查询语句之中,嵌套若干个不同功能的小查询,从而一起完成复杂查询的一种编写形式,通俗一点就是包含select嵌套的查询。
(2)特点:子查询可以返回的数据类型一共分为四种
单行单列:返回的是一个具体列的内容,可以理解为一个单值数据;
-- 查询年纪最大的员工信息,显示信息包含员工号,员工名字,员工年龄
-- 1.查询最大年龄
select max(age) from emp3; -- 85
-- 2.让每个员工的年龄与最大年龄进行比较,相等则满足条件
select * from emp3 where age = (select max(age) from emp3);-- 单行单列
单行多列:返回一行数据中多个列的内容;
多行单列:返回多行记录之中同一列的内容,相当于给出了一个操作范围;
-- 查询研发部和销售部的员工信息,包含员工号,员工名字
-- 方式一:关联查询
select * from dept3 join emp3 on deptno = dept_id and(name = '研发部'or name = '销售部');
-- 方式二:子查询
-- (1)先查询研发部和销售部的部门号:deptno 1001和1002
select deptno from dept3 where name = '研发部'or name = '销售部'; -- 多行单列
-- (2)查询那个员工的部门号是1001或1002
select * from emp3 where dept_id in (select deptno from dept3 where name = '研发部'or name = '销售部');
多行多列:查询返回的是一张临时表;
-- 查询研发部30岁以下的员工信息,包括员工号,员工名字,部门名字
-- 方式一:关联查询
select * from dept3 join emp3 on deptno = dept_id and (name = '研发部'and age <20);
-- 方式二:子查询
-- (1)在部门表中查询研发部信息
select * from dept3 where name = '研发部'; -- 单行多列
-- (2)在员工表中查询年龄小于30岁的员工信息
select * from emp3 where age <30;
-- (3)将以上两个表的结果进行关联查询
seect * from (select * from dept3 where name = '研发部') t1 join (select * from emp3 where age <30) t2 on t1.deptno = t2.dept_id; -- 多行多列
(3)子查询关键字
在子查询中,有一些常用的逻辑关键字,这些关键字可以给我们提供更丰富的查询功能,主要关键字如下:
<1>all关键字
<2>any关键字
-- 查询年龄大于‘1003’部门任意一个员工年龄的员工信息
select * from emp3 where age > any(select age from emp3 where dept_id = '1003')and dept_id !='1003';
select * from emp3 where age > any(select age from emp3 where dept_id = '1003')some dept_id !='1003';
<3>some关键字
<4>in关键字
<5>exists关键字
-- exists关键字
select * from emp3 where exists(select 1); -- 全表输出
select * from emp3 where exists(select * from emp3); -- 全表输出
-- 查询公司是否有大雨60岁的员工,有则输出
select * from emp3 a where exists(select * from emp3 where a.age > 60);
select * from emp3 a where eid in(select eid from emp3 where a.age > 60);
-- 查询有所属部门的员工信息
select * from emp3 a where exists(select * from dept3 b where a.dept_id = b.deptno);
select * from emp3 a where dept_id in(select deptno from dept3 b where a.dept_id = b.deptno);
7.自关联查询
-- 进行关联查询
-- 1.查询每个三国人物及他的上级信息,如: 关羽 张飞
select * from t_sanguo a,t_sanguo b where a.manager_id = b.eid;
select a.ename,b.ename from t_sanguo a,t_sanguo b where a.manager_id = b.eid;
select a.ename,b.ename from t_sanguo a join t_sanguo b on a.manager_id = b.eid;
-- 2.查询所有人物及上级
select a.ename,b.ename from t_sanguo a left join t_sanguo b on a.manager_id = b.eid;
-- 3.查询所有人物及上级,上上级
select
a.ename,b.ename,c.ename
from t_sanguo a
left join t_sanguo b on a.manager_id = b.eid
left join t_sanguo c on b.manager_id = c.eid;