前面的DQL,即数据查询语言的案例全部是单表查询,但是实际运用中全部是一堆表进行查询,总的来说一共有三种表关系:一对一、一对多、多对多
数据准备
-- 创建dept表,并插入数据
create table dept(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '部门名称'
)comment '部门表';
-- 插入数据
INSERT INTO dept (id, name)
VALUES (1, '研发部'),
(2, '市场部'),
(3, '财务部'),
(4, '销售部'),
(5, '总经办'),
(6, '人事部');
-- 创建emp表
create table emp(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '姓名', age int comment '年龄',
job varchar(20) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直属领导ID',
dept_id int comment '部门ID'
)comment '员工表';
-- 添加外键
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
-- 插入数据
INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id)
VALUES (1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),
(2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
(3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
(4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
(5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
(6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),
(7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
(8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
(9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),
(10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
(11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
(12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
(13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),
(14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
(15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
(16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
(17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);
多表查询原理-笛卡尔积
select * from emp,dept;
直接进行查询的话获得的结果是两个表的笛卡尔积
我们进行多表查询的目的就是消除其中的无效信息
连接查询(集合运算)
内连接
结果为交集部分,即绿色部分
# 是否明显地声明是内连接
# 隐式连接
SELECT 字段列表 FROM 表1 , 表2 WHERE 条件 ... ;
# 显式连接
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ... ;
内连接案例
# 查询每一个员工的姓名 , 及关联的部门的名称
# 表结构: emp , dept
# 连接条件: emp.dept_id = dept.id
# 隐式内连接实现
select emp.name , dept.name
from emp , dept
where emp.dept_id = dept.id;
-- 为每一张表起别名,简化SQL编写
select e.name,d.name
from emp e , dept d
where e.dept_id = d.id;
# 显式内连接实现
select e.name, d.name
from emp e
inner join dept d
on e.dept_id = d.id;
-- 为每一张表起别名,简化SQL编写
select e.name, d.name
from emp e
join dept d
on e.dept_id = d.id;
外连接
外连接分为左外连接和右外连接是可以相互替换的,只需要调整在连接查询时SQL中,表结构的先后顺序就可以了。而我们在日常开发使用时,更偏向于左外连接。
左外连接
结果为蓝色和绿色部分
SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件 ...;
右外连接
结果为绿色和黄色部分
SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件 ...;
外连接案例
# 查询emp表的所有数据, 和对应的部门信息(左外连接)
# 由于需求中提到,要查询emp的所有数据,所以是不能内连接查询的,需要考虑使用外连接查询。
# 表结构: emp, dept
# 连接条件: emp.dept_id = dept.id
select e.*, d.name as dept_name
from emp as e left outer join dept d
on e.dept_id=d.id;
# 查询dept表的所有数据, 和对应的员工信息(右外连接)
# 由于需求中提到,要查询dept表的所有数据,所以是不能内连接查询的,需要考虑使用外连接查 询。
# 表结构: emp, dept
# 连接条件: emp.dept_id = dept.id
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
select d.*, e.* from dept d left outer join emp e on e.dept_id = d.id;
自连接
自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次。
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
自连接案例
# 查询员工 及其 所属领导的名字
# 表结构: emp
select a.name as worker_name, b.name as manager_name
from emp a, emp b
where a.managerid = b.id;
# 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来
# 表结构: emp a , emp b
select a.name as worker_name, b.name as manager_name
from emp a left join emp b
on a.managerid = b.id;
联合查询
对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。
对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。
union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;
联合查询案例
# 将薪资低于 5000 的员工,和年龄大于 50 岁的员工全部查询出来.
# 当前对于这个需求,我们可以直接使用多条件查询,使用逻辑运算符 or 连接即可。
# 那这里呢,我们 也可以通过union/union all来联合查询.
# 使用逻辑运算
select *
from emp
where salary<5000 or age>50;
# 使用union
select *
from emp
where salary<5000
union
select *
from emp
where age>50
子查询
标量子查询(查询结果为单个值)
# 查询“销售部”的所有员工信息
# 使用内连接
select emp.*
from emp,dept
where emp.dept_id = dept.id and dept.name = '销售部';
# 使用标量子查询
select *
from emp
where dept_id = (select id from dept where name = '销售部');
# 查询比 "方东白" 入职晚的员工信息
select *
from emp
where entrydate > (select entrydate from emp where name = '方东白');
列子查询(查询结果为一列)
# 查询 "销售部" 和 "市场部" 的所有员工信息
# 使用流程控制
select emp.*
from emp,dept
where emp.dept_id = dept.id and (dept.name = '销售部' or dept.name = '市场部');
#使用列子查询
select *
from emp
where dept_id in (select id from dept where name in ('销售部','市场部'));
行子查询(查询结果为一行)
# 查询与 "张无忌" 的薪资及直属领导相同的员工信息;
select * from emp where (salary,managerid) = (select salary, managerid from emp where name = '张无忌');
表子查询(查询结果为多行多列)
# 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job,salary) in ( select job, salary from emp where name = '鹿杖客' or name = '宋远桥' );