MySQL --- 多表查询 - 七种JOINS实现、集合运算、多表查询练习

七种JOINS实现

左上图的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;

补充

USING 指定条件,等值条件的两个字段都是一模一样的,既然是一模一样的,能不能只写一个字段呢?答案是肯定的,USING 可以让 MySQL 自己在表中找这个字段给我们建立关联

-- 内连接
-- 隐式内连接
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);

自连接:一张表做等值查询,把一张表作为两张表做等值查询,由于字段不一样,所以不能使用 USING

每一个员工都有领导编号 mgr,领导的信息也在这张表当中

-- 自连接 由于两张表名一样 只能通过取别名区分

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

  1. 会删除重复行

  2. 相同的行在结果中只出现一次

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;

整个表有 15 条记录

部门编号为 10 有 3 条记录 

求并集有 15 条记录,UNION 后面不加默认为 DISTINCT

UNION ALL

  1. 不会删除重复行

  2. 相同的行在结果中可能出现多次

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;

MySQL --- 数据库查询 - 排序查询、分页查询

错误用法

SELECT * FROM emp ORDER BY emp.job
UNION ALL
SELECT * FROM emp WHERE deptno=10;

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

SELECT emp.empno FROM emp 
UNION ALL
SELECT * FROM emp WHERE deptno=10;

第一个表只有 1 列

第二个表的列数正常  

(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相等

多表查询练习

  1. 查询出雇佣日期在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;

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qiuqiuyaq

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值