
左上图的JOIN是左外连接,右上图的JOIN是右外连接,中间图的JOIN是内连接,左中图的JOIN在左上图的基础上再去掉中间重复的,只需要 A 在 B 中没有的部分,右中图的JOIN在右上图的基础上再去掉中间重复的,只需要 B 在 A 中没有的部分,左下角是全外连接
- -- 左上 左外连接
- SELECT * FROM
- emp LEFT JOIN dept
- ON emp.deptno=dept.deptno;

- -- 右上 右外连接
- SELECT * FROM
- emp RIGHT JOIN dept
- ON emp.deptno=dept.deptno;

- -- 左中 在左外连接的基础上
- SELECT * FROM
- emp LEFT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL;

- -- 右中 在右外连接的基础上
- SELECT * FROM
- emp RIGHT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL;

全外连接可以使用以上的两张图片拼接而成,一共有 3 种方式可以实现
接下来用左上图和右中图来实现全外连接
- -- 左上 左外连接
- SELECT * FROM
- emp LEFT JOIN dept
- ON emp.deptno=dept.deptno -- 去掉;
- UNION -- 求并集
- -- 右中 在右外连接的基础上
- SELECT * FROM
- emp RIGHT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL;

使用右上图和左中图来实现全外连接
- -- 右上 右外连接
- SELECT * FROM
- emp RIGHT JOIN dept
- ON emp.deptno=dept.deptno -- 去掉;
- UNION -- 求并集
- -- 左中 在左外连接的基础上
- SELECT * FROM
- emp LEFT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL;

右下图是在全连接的基础上去掉中间部分,或者用左中图和右中图拼接
- -- 右下图用左中图和右中图拼接
- -- 左中 在左外连接的基础上
- SELECT * FROM
- emp LEFT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL -- 去掉;
- UNION
- -- 右中 在右外连接的基础上
- SELECT * FROM
- emp RIGHT JOIN dept
- ON emp.deptno=dept.deptno
- -- 去掉满足连接条件的记录→只需要不满足条件的→连接条件不匹配或者为NULL
- WHERE emp.deptno IS NULL;

补充
- -- 内连接
- -- 隐式内连接
- SELECT *
- FROM emp,dept
- WHERE emp.deptno=dept.deptno;
-
- -- 显示内连接 语法不同
- SELECT *
- FROM emp INNER JOIN dept
- ON emp.deptno=dept.deptno;
-
- -- 使用USING来关联相同的字段
- SELECT *
- FROM emp JOIN dept
- USING(deptno);

自连接:一张表做等值查询,把一张表当做两张表查询
- -- 自连接 由于两张表名一样 所以只能取别名
-
- SELECT *
- FROM emp e, emp m
- -- 对应的领导信息
- WHERE e.mgr=m.empno;

- -- 自连接 由于两张表名一样 所以只能取别名
-
- SELECT e.empno,e.ename,m.ename 领导
- FROM emp e,emp m
- -- 对应的领导信息
- WHERE e.mgr=m.empno;

MySQL只支持并集运算。
并集即两个集合所有部分

UNION DISTINCT
会删除重复行
相同的行在结果中只出现一次
A={A,B,C},B={C,D,E}
UNION DISTINCT C={A,B,C,D,E},有两个 C,重复的 C 只保留一个
- SELECT * FROM emp
- UNION
- SELECT * FROM emp WHERE deptno=10;
部门编号为 10 有 3 条记录
整个表有 15 条记录
求并集有 15 条记录,UNION 后面不加默认为 DISTINCT

UNION ALL
不会删除重复行
相同的行在结果中可能出现多次
A={A,B,C},B={C,D,E}
UNION ALL C={A,B,C,C,D,E},会保留重复的
- SELECT * FROM emp
- UNION ALL
- SELECT * FROM emp WHERE deptno=10;
求并集,保留重复的,共 15 + 3 = 18 条
要求
(1)输入的查询不能包含ORDER BY字句,可以为整个集合运算结果选择性地增加一个ORDER BY字句
- SELECT * FROM emp
- UNION ALL
- SELECT * FROM emp WHERE deptno=10
- -- 按姓名查询 对整个临时表排序而不是对emp表排序
- ORDER BY ename;

错误用法
- SELECT * FROM emp ORDER BY emp.job
- UNION ALL
- SELECT * FROM emp WHERE deptno=10;

(2)两个查询必须包含相同的列数

第一个表只有 1 列,第二个表的列数正常
- SELECT emp.empno FROM emp
- UNION ALL
- SELECT * FROM emp WHERE deptno=10;


(3)相应列必须具有兼容的数据类型。兼容个的数据类型:优先级较低的数据类型必须能隐式地转换为较高级的数据类型。比如输入的查询1的第一列为int类型,输入的查询2的第一列为float类型,则较低的数据类型int类型可以隐式地转换为较高级float类型。如果输入的查询1的第一列为char类型,输入的查询2的第一列为datetime类型,则会提示转换失败:从字符串转换日期和/或时间时,转换失败
(4)集合运算结果中列名由输入的查询1决定,如果要为结果分配结果列,应该在输入的查询1中分配相应的别名
- SELECT emp.empno,emp.ename FROM emp
- UNION ALL
- SELECT emp.empno 'id',emp.ename 'name' FROM emp WHERE deptno=10;
第一种情况在输入的查询2取别名,结果没有改变

- SELECT emp.empno 'id',emp.ename 'name' FROM emp
- UNION ALL
- SELECT emp.empno,emp.ename FROM emp WHERE deptno=10;
第二种情况在输入的查询1取别名,结果改变
(5)集合运算时,对行进行比较时,集合运算认为两个NULL相等
查询出雇佣日期在1981年的所有员工的编号、姓名、雇佣日期、工作、领导姓名、雇佣月工资、雇佣年工资(基本工资+奖金),工资等级、部门编号、部门名称、部门位置,并且求这些员工的月基本工资在1500~3500之间,将最后的结果按照年工资的降序排列,如果年工资相等,则按照工作进行排序。
需求很多,逐步分析
确定所需要的数据表
emp:编号、姓名、雇佣日期,工作、月工资、年薪
emp:领导姓名
dept:部门编号、名称、位置
salgrade:工资等级
确定一致的关联字段
员工和领导:e.mgr=e1.empno
员工和部门:e.deptno=dept.deptno
员工和工资等级:e.sal BETWEEN s.losal AND s.hisal

步骤一:查询出所有在1981年雇佣的雇员编号、姓名、御用日期、工作、月工资、年工资,并且月薪在1500~3500之间。只需要emp单张表即可

奖金有空的部分,年薪最后的结果为空,需要判断奖金为空,就把它变成0

- #获取1981年入职员工的信息并且满足薪资在1500~3500之间
- SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪
- FROM emp e
- WHERE e.sal BETWEEN 1500 AND 3500;

步骤二:加入领导信息,使用自身关联
- -- 获取1981年入职员工的信息并且满足薪资在1500~3500之间
- SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪,
- m.ename 领导
- FROM emp e,emp m
- WHERE e.sal BETWEEN 1500 AND 3500
- -- 获取领导姓名
- AND e.mgr=m.empno;

步骤三:加入部门信息
- -- 获取1981年入职员工的信息并且满足薪资在1500~3500之间
- SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪,
- m.ename 领导,d.deptno,d.dname,d.loc
- FROM emp e,emp m,dept d
- WHERE e.sal BETWEEN 1500 AND 3500
- -- 获取领导姓名
- AND e.mgr=m.empno
- -- 加入部门信息
- AND e.deptno=d.deptno;
步骤四:加入工资等级
- -- 获取1981年入职员工的信息并且满足薪资在1500~3500之间
- SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪,
- m.ename 领导,d.deptno,d.dname,d.loc,s.grade 工资等级
- FROM emp e,emp m,dept d,salgrade s
- WHERE e.sal BETWEEN 1500 AND 3500
- -- 获取领导姓名
- AND e.mgr=m.empno
- -- 加入部门信息
- AND e.deptno=d.deptno
- -- 加入工资等级
- AND e.sal BETWEEN s.losal AND s.hisal;

步骤五:排序
最后的结果按照年工资的降序排列,如果年工资相等,则按照工作进行排序。
- -- 获取1981年入职员工的信息并且满足薪资在1500~3500之间
- SELECT e.empno,e.ename,e.hiredate,e.job,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪,
- m.ename 领导,d.deptno,d.dname,d.loc,s.grade 工资等级
- FROM emp e,emp m,dept d,salgrade s
- WHERE e.sal BETWEEN 1500 AND 3500
- -- 获取领导姓名
- AND e.mgr=m.empno
- -- 加入部门信息
- AND e.deptno=d.deptno
- -- 加入工资等级
- AND e.sal BETWEEN s.losal AND s.hisal
- -- 排序
- ORDER BY 年薪 DESC,e.job ASC;
