主题
多表查询 
多表关系 
- 一对多(多对一)
- 案例:员工 <==> 部门
- 关系:一个员工只能属于一个部门,一个部门却可以包含多个员工
- 实现:在多的一方建立外键(此处为部门表),指向一的那方的主键
- 多对多
- 案例:学生 <==> 课程
- 关系:一个学生可以选修门课程,一门课程可以被多个学生选修
- 实现:建立一张中间表,中间表至少包含两个外键,分别关联两方的主键
- 一对一
- 案例:用户 <==> 用户详情
- 关系:一对一的关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放到另一张表中,以提升操作效率
- 实现:在任意一方加入外键,关联另一方的主键,并且设置外键为唯一的(unique)
多表查询介绍 
注意:多表查询的时候,需要消除笛卡尔积,也就是 where 后面需要跟上关联关系条件, 例如:select * from emp,dept where emp.dept_id=dept.id;
小知识
笛卡尔积:指在数学中,两个集合的所有组合情况。

多表查询分类 
- 连接查询
- 内连接:查询两表交集部分
- 外连接 - 左外连接:查询左表所有数据+两表交集部分数据
- 右外连接:查询由表所有数据+两表交集部分数据
 
- 自连接:当前表与自身的连接查询,自连接必须使用表别名
- 子查询

内连接查询 
语法 
- 隐式内连接 查询的是两表的交集部分
sql
select 字段列表 from 表1,表2 where 条件;- 显示外连接
sql
select 字段列表 from 表1 inner join 表2 on 连接条件...;inner关键字可以省略;
案例: 
sql
-- 查询每一个员工的姓名,及关联的部门的名称
-- 隐式内连接
select emp.name as empName,dept.name as deptName  from emp,dept where emp.dept_id = dept.id;
-- 表用别名
select e.name as empName,d.name as deptName  from emp e,dept d where e.dept_id = d.id;
-- 显示内连接
select emp.name as empName, dept.name as deptName from emp inner join dept on emp.dept_id=dept.id;
-- 表用别名
select e.name as empName, d.name as deptName from emp e inner join dept d on e.dept_id=d.id;外连接查询 
语法 
关键字 outer 可以省略;
- 左外连接
sql
select 字段列表 from 表1 left outer join 表2 on 条件;包含表 1(左表) + 表 1 表 2 交集部分的数据
- 右外连接
sql
select 字段列表 from 表1 right outer join 表2 on 条件;包含表 2(右表) + 表 1 表 2 交集部分的数据
自连接查询 
- 语法
sql
-- 外联查询
select 字段列表 from 表A 别名A join 表A 别名B on 条件;
--内连查询
select 字段列表 from 表A 别名A, 表A 别名B where 条件;提示
- 自连接查询,可以是内连接查询,也可以是外连接查询。
- 自连接查询,表必须起别名
- 示例: 员工表(emp)内有一列(managerid)是员工直属领导的 ID,
需求 1:查询员工信息以及自己直属领导的名字;
sql
-- 查询员工信息以及自己直属领导的名字
-- 用外联
select e2.*,e1.name as managerName from emp e1 join emp e2 on e1.id=e2.managerid;
-- 用内联
select e2.*,e1.name as managerName from emp e1, emp e2 where e1.id=e2.managerid;需求 2:查询所有员工极其领导的名字,若员工没有领导也要查询出来
此时,只能用外连接,因为内连接只能查询两表交集部分,只有外连接才能查某一张表的全部数据
sql
select e1.name empName, e2.name managerName from emp e1 left outer join emp e2 on e1.managerid=e2.id;联合查询 
指的是将多次查询的结果合并起来,形成一个新的查询结果集;
- 关键字: - union或者- union all
- 语法 
sql
selsct 字段列表 from 表A ... union [all] select 字段列表 from biao B;提示
- union与- union all的区别
- union all是直接将查询的结果合并起来
- union将结果合并后会进行去重
- 联合查询,必须保证联合的两个查询语句字段列表数量和类型完全一致
子查询 
- 概念: - sql 语句中嵌套的 select 语句,称为嵌套查询,也叫子查询 
- 示例: 
sql
select * from ti where column = (select column1 from t2);上述 sql 语句中括号内的 select 语句就是子查询
提示
子查询可以出现在insert、update、delete、select 的任何一种 sql 语句中
- 子查询分类
- 根据查询结果分:
- 标量子查询(子查询得到的结果为单个值)
- 列子查询(子查询得到的结果为一个列)
- 行子查询(子查询得到的结果为一行)
- 表子查询(子查询结果为多行多列)
- 根据子查询的位置分:
- where 之后
- from 之后
- select 之后
- 标量子查询 
子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式
常用的操作符:
| 操作符 | 说明 | 
|---|---|
| > | 大于 | 
| < | 小于 | 
| = | 等于 | 
| >= | 大于等于 | 
| <= | 小于等于 | 
| <> | 不等于 | 
- 案例 1:查询“销售部”所有的员工信息
sql
select * from emp where dept_id = (select id from dept where name = '销售部');- 案例 2:查询在‘令狐冲’后面入职的员工信息
sql
select * from emp where entrydate > (select entrydate from emp where name='令狐冲');- 列子查询 
子查询返回的结果是一列
常用操作符
| 操作符 | 说明 | 
|---|---|
| in | 在指定集合范围内,多选一 | 
| not in | 不在指定集合范围内 | 
| any | 子查询返回列表中,有任意一个满足条件即可 | 
| some | 与 any等同,使用some的地方都可以使用any | 
| all | 子查询返回的所有值都必须全部满足 | 
- 案例 1:查询销售部和市场部所有员工信息
sql
select * from emp where dept_id in (
  select id from dept where name = '销售部' or name = '市场部'
  );案例 2:查询比财务部所有人工资都高的员工信息
sql
-- all实现
select * from emp where salary > all(select salary from emp where dept_id = (select id from dept where name = '财务部'));
-- max 实现
select * from emp where salary > (select max(salary) from emp where dept_id = (select id from dept where name = '财务部'));- 行子查询 
子查询返回结果是一行
常用操作符:=、<>、in、not in
- 案例 1:查询与“张无忌”薪资及直属领导相同的员工信息
sql
select * from emp where (salary, managerid ) = (select salary, managerid from emp where name = '张无忌');TIP
(salary, managerid ) = (12500,2)等价于salary=12500 and managerid=2
- 表子查询 
子查询返回的结果有多行多列(可以看作返回的结果为一张表)
表子查询经常出现在
from之后,将子查询的结果作为一张临时表,在与其他表一起做联查操作
常用操作符:in
- 案例 1:查询与‘宋江’ ‘林冲’ 职位和薪资相同的所有员工信息
sql
SELECT
	*
FROM
	emp
WHERE
	( job, salary ) IN ( SELECT job, salary FROM emp WHERE NAME = '宋江' OR NAME = '林冲' );TIP
(job, salary) in (select job,salary from emp where name = '宋江' or name = '林冲') 表示 job, salary两个值只需要满足后面 sql 语句的其中一条数据就能匹配上查询结果,因为 后面的 sql 语句查询出的结果为两条数据
- 案例 2:查询入职日期是‘2006-01-01’之后的员工信息,及其部门名称
sql
SELECT
	e.*,
	d.`name` dept_name
FROM
	( SELECT * FROM emp WHERE entrydate > '2002-01-01' ) e
	LEFT JOIN dept d ON e.dept_id = d.id;多表查询案例 
- 查询员工的姓名、年龄、职位、部门信息。
sql
SELECT
	e.name username,
	e.age age,
	e.job job,
	e.dept_id dept_id,
	d.name dept_name
FROM
	emp e
	LEFT JOIN dept d ON e.dept_id = d.id;- 查询年龄小于 30 岁的员工姓名、年龄、职位、部门信息。
sql
SELECT
	e.name username,
	e.age age,
	e.job job,
	e.dept_id dept_id,
	d.name dept_name
FROM
	emp e
	LEFT JOIN dept d ON e.dept_id = d.id
WHERE e.age < 30;- 查询拥有员工的部门 ID、部门名称。
sql
SELECT
	*
FROM
	dept
WHERE
	id IN ( SELECT dept_id FROM emp GROUP BY dept_id );
-- 或者
select distinct d.id, d.name from emp e, dept d where e.dept_id = d.id;- 查询所有年龄大于 40 岁的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来。
sql
SELECT
	e.*,
	d.`name` dept_name
FROM
	emp e
	LEFT JOIN dept d ON e.dept_id = d.id
WHERE
	e.age > 40;- 查询所有员工的工资等级。
sql
-- 涉及一张工资等级表grade,工资等级表拥有字段 level, low, high
SELECT
	e.*,
	g.LEVEL salaryLevel
FROM
	emp e,
	grade g
WHERE
	e.salary BETWEEN g.low
	AND g.high;- 查询“研发部”所有员工的信息及工资等级。
sql
SELECT
	e.*,
	g.lenvl
FROM
	emp e,
	dept d,
	ggrade g
WHERE
	d.`name` = '研发部'
	AND ( e.salary BETWEEN g.low AND g.high )
	AND e.dept_id = d.id;- 查询“研发部”员工的平均工资。
sql
SELECT
	AVG(e.salary) sum_salary
FROM
	emp e,
	dept d
WHERE
	e.dept_id = d.id
	AND d.`name` = '研发部';- 查询工资比“灭绝”高的员工信息。
sql
SELECT *  FROM emp WHERE salary > (SELECT salary FROM emp WHER `name` = '灭绝');- 查询比平均薪资高的员工信息。
sql
SELECT * FROM emp WHERE salary > (SELECT AVG(salary) FROM emp);- 查询工资比所在部门平均工资低的员工信息。
sql
SELECT
	*
FROM
	emp AS e1
WHERE
	e1.salary < ( SELECT avg( e2.salary ) FROM emp AS e2 WHERE e2.dept_id = e1.dept_id );- 查询所有的部门信息,并统计部门的员工人数。
sql
-- 展示了子查询在select后面的情况
SELECT
	d.id,
	d.`name`,
	( SELECT count(*) FROM emp e WHERE e.dept_id = d.id ) emp_count
FROM
	dept d;- 查询所有学生的选课情况,展示出学生名称,学号,课程名称(多对多)
sql
/*
表:student、course、student_course
连接条件:
student.id = student_course.student_id
course.id = student_course.course_id
*/
select
	s.`name` student_name,
	s.`no` student_no,
	c.`name` course_name
from
	student s,
	course c,
	student_course sc
where
	s.id=sc.student_id
	and c.id=sc.course_id;