前言:
- 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于MYSQL的使用讲解,大概应该会更新30篇+,涵盖入门、进阶、高级(一些原理分析);
- 这一篇是内容比较多,涵盖:多表关系、多表查询(交叉连接、内连接、外连接、自连接)、七种JSONS、集合基础讲解;
- 虽然MYSQL命令很多,但是自己去多敲一点,到后面忘记了,查一下就可以回忆起来使用了;
- 这一系列也是本人学习MYSQL做的笔记,也是为了方便后面忘记查询;
- 参考资料:尚硅谷、黑马、csdn和知乎博客;
- 欢迎收藏 + 关注,本人将会持续更新。
文章目录
课程使用数据文件: 后台私信获取。
多表关系
项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:
- 一对多(多对一)
- 多对多
- 一对一
以上关系就是E-R图中的'1' 和 'n' / '*'含义
一对多(多对一)
案例:部门与员工的关系
关系:一个部门对应多个员工,一个员工对应一个部门
实现:在多的一方建立外键,关联另一方的主键
多对多
案例:学生与课程的关系
关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择
实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
一对一
案例:用户与用户详细的关系
关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率。
实现:在任意一方加入一个外键,关联另一方的主键,并且设置外键为唯一约束
多表查询
- 前面我们使用的查询,只是对单表进行查询,在具体的应用中,经常需要实现在一个查询语句中显示多张数据表的数据,这就是所谓的多表查询。
- 前提条件 :发生在两个及两个以上的表之间,表与表之间有关系(一对多、多对多、一对一),且他们之间一定的关联字段。
- 实现:首先将两个或两个以上的表按照某个条件连接起来,然后再查询到所要求的数据记录。
- 优点:方便信息管理,提高查询效率。
😍 连接查询方式:分为交叉连接 、内连接 、外连接 查询三种,在介绍这些查询方式之前我们首先来介绍一下笛卡尔积。
笛卡尔积概念:描述两个集合X和Y的所有组合,如图所示:
上面是数学概念,结合MYSQL实践如下.
在mysql中多表一起查询的时候,这个时候遵循笛卡尔积,如下案例:
mysql
# 员工表
mysql> select * from emp;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 |
+-------+--------+-----------+------+------------+------+------+--------+
14 rows in set (0.02 sec)
# 部门表
mysql> select * from dept;
+--------+------------+----------+
| deptno | dname | loc |
+--------+------------+----------+
| 10 | ACCOUNTING | NEW YORK |
| 20 | RESEARCH | DALLAS |
| 30 | SALES | CHICAGO |
| 40 | OPERATIONS | BOSTON |
+--------+------------+----------+
4 rows in set (0.01 sec)
这个时候查询员工名字和其所对应的部门名字的时候,会发生:
mysql
mysql> select ename, dname from emp, dept;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | OPERATIONS |
| SMITH | SALES |
| SMITH | RESEARCH |
| SMITH | ACCOUNTING |
| ALLEN | OPERATIONS |
| ALLEN | SALES |
| ALLEN | RESEARCH |
| ALLEN | ACCOUNTING |
| WARD | OPERATIONS |
| WARD | SALES |
| WARD | RESEARCH |
| WARD | ACCOUNTING |
| JONES | OPERATIONS |
| JONES | SALES |
| JONES | RESEARCH |
| JONES | ACCOUNTING |
| MARTIN | OPERATIONS |
| MARTIN | SALES |
| MARTIN | RESEARCH |
| MARTIN | ACCOUNTING |
| BLAKE | OPERATIONS |
| BLAKE | SALES |
| BLAKE | RESEARCH |
| BLAKE | ACCOUNTING |
| CLARK | OPERATIONS |
| CLARK | SALES |
| CLARK | RESEARCH |
| CLARK | ACCOUNTING |
| SCOTT | OPERATIONS |
| SCOTT | SALES |
| SCOTT | RESEARCH |
| SCOTT | ACCOUNTING |
| KING | OPERATIONS |
| KING | SALES |
| KING | RESEARCH |
| KING | ACCOUNTING |
| TURNER | OPERATIONS |
| TURNER | SALES |
| TURNER | RESEARCH |
| TURNER | ACCOUNTING |
| ADAMS | OPERATIONS |
| ADAMS | SALES |
| ADAMS | RESEARCH |
| ADAMS | ACCOUNTING |
| JAMES | OPERATIONS |
| JAMES | SALES |
| JAMES | RESEARCH |
| JAMES | ACCOUNTING |
| FORD | OPERATIONS |
| FORD | SALES |
| FORD | RESEARCH |
| FORD | ACCOUNTING |
| MILLER | OPERATIONS |
| MILLER | SALES |
| MILLER | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
56 rows in set (0.00 sec)
查询到很多,一共 16 * 4 = 56条,这个就是笛卡尔积带来的问题,而解决这些问题,可以有如下;
交叉连接
交叉连接:返回被连接的两个表所有数据行的笛卡尔积
交叉连接不带WHERE子句,它返回被连接的两个表所有数据行的笛卡尔积
- 查询员工及员工所在的部门信息
mysql
SELECT * FROM emp,dept;
SELECT * FROM emp CROSS JOIN dept;
如果不加条件直接进行查询,则数据条数是两个表记录条数的乘积,这种结果就是我们上面介绍的 笛卡尔乘积。
笛卡尔乘积公式 : A表中数据条数 * B表中数据条数 = 笛卡尔乘积
如果两张表的数据量都很大,那么这种庞大时很可怕的,想要消除笛卡尔积的无效记录,需要使用关联字段。
范例:利用等值条件来处理笛卡尔积
mysql
mysql> select ename, dname from emp e, dept d where e.deptno=d.deptno;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)
这样一共查出来14条,比上面不加限制查出的56条少了很多。
这个等值限制,就是我们接下要说的,内连接。
内连接
在表关系的笛卡尔积数据记录中,保留表关系中所有匹配的数据记录,舍弃不匹配的数据记录。按匹配的条件可以分成等值连接和不等值连接。
有两种,显式的和隐式的,返回连接表中符合连接条件和查询条件的数据行。
- 隐式内连接
mysql
SELECT * FROM 表1,表2 WHERE 条件; # 本人喜欢使用
- 显示内连接(使用关键字INNER JOIN)
mysql
SELECT * FROM 表1 [INNER] JOIN 表2 ON 条件;
# 上面案例用这种方式写一下,效果一样
mysql> select ename, dname from emp e inner join dept d on e.deptno=d.deptno;
+--------+------------+
| ename | dname |
+--------+------------+
| SMITH | RESEARCH |
| ALLEN | SALES |
| WARD | SALES |
| JONES | RESEARCH |
| MARTIN | SALES |
| BLAKE | SALES |
| CLARK | ACCOUNTING |
| SCOTT | RESEARCH |
| KING | ACCOUNTING |
| TURNER | SALES |
| ADAMS | RESEARCH |
| JAMES | SALES |
| FORD | RESEARCH |
| MILLER | ACCOUNTING |
+--------+------------+
14 rows in set (0.00 sec)
等值连接
在连接条件中==使用等于号(=)==运算符比较被连接列的列值,
- 查询员工及员工部门信息
mysql
#隐式连接
SELECT * FROM emp e,dept d WHERE e.deptno=d.deptno;
#显示连接
SELECT * FROM emp e INNER JOIN dept d ON e.deptno=d.deptno;
- 等值连接可以使用USING来自动关联两表中相同的列
mysql
SELECT * FROM emp INNER JOIN dept USING(deptno);
非等值连接
在连接条件使用除等于运算符以外的其它比较运算符比较被连接的 列的列值。这些运算符包括>、>=、<=、<、!>、!<和<>等。
依然分为隐式连接 和显示连接
- 查询员工工资级别
mysql
#隐式连接
SELECT * FROM emp e,salgrade s WHERE e.sal BETWEEN s.losal AND s.hisal;
#显示连接
SELECT * FROM emp e INNER JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;
/*
mysql> select e.ename, s.grade from emp e inner join salgrade s on e.sal between s.losal and s.hisal;
+--------+-------+
| ename | grade |
+--------+-------+
| SMITH | 1 |
| ALLEN | 3 |
| WARD | 2 |
| JONES | 4 |
| MARTIN | 2 |
| BLAKE | 4 |
| CLARK | 4 |
| SCOTT | 4 |
| KING | 5 |
| TURNER | 3 |
| ADAMS | 1 |
| JAMES | 1 |
| FORD | 4 |
| MILLER | 2 |
+--------+-------+
14 rows in set (0.00 sec)
*/
外连接
在表关系的笛卡尔积中,不仅保留表关系中所有匹配的数据记录,而且还保留部分不匹配的记录。按照保留不匹配条件数据记录来源可以分为左外连接(LEFT OUTER JOIN)、右外连接(RIGHT OUTER JOIN)和全外连接(FULL OUTER JOIN)。
外连接使用语法如下:
mysql
SELECT * FROM 表1 LEFT|RIGHT|FULL [OUTER] JOIN 表2 ON 条件;
左外连接
在表关系的笛卡尔积中,出了选择相匹配的数据记录,还包含关联左边表中不匹配的数据记录。
不仅查询出匹配的数据,还查询出不匹配的数据,这个时候可以看做是以左边表为主,去匹配右边数据,在右边查找。
案例:
准备:插入一条部门编号为NULL的数据(关于插入后面会讲)
mysql
INSERT INTO emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) VALUES(6666,'yxz','student',7369,CURDATE(),800,100,NULL);
案例:
- 查询员工及对应的部门信息
- 这个时候没有部门的员工也显示出来,当然没有员工的部门不会显示,因为是这一条查询以员工表为主去找去部门表查询匹配,如果查询不到,则也会显示该员工的信息,只是没有部门信息而已,详细请看:
mysql
SELECT * FROM emp e LEFT OUTER JOIN dept d ON e.deptno=d.deptno;
/*
如下:刚插入的没有部门表,但是左边是员工表,员工表为主么,所以员工信息还会显示出来,但是部门信息不会。
mysql> select * from emp e left outer join dept d on e.deptno=d.deptno;
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | dname | loc |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL | NULL | NULL | NULL |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 | 30 | SALES | CHICAGO |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 | 30 | SALES | CHICAGO |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 | 30 | SALES | CHICAGO |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 | 30 | SALES | CHICAGO |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 | 30 | SALES | CHICAGO |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 | 30 | SALES | CHICAGO |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
15 rows in set (0.00 sec)
*/
右外连接
在表关系的笛卡尔积中,出了选择相匹配的数据记录,还包含关联右边表中不匹配的数据记录。
这个就是和上面左外连接相反,以第二个表为主,去第一个表找东西,找不到的就在左半边显示全部是空。
案例:
- 查询员工及对应的部门信息
- 这个时候就是主要有部门,都显示,员工没有部门,不做显示:
mysql
SELECT * FROM emp e RIGHT OUTER JOIN dept d ON e.deptno=d.deptno;
/*
mysql> select * from emp e right outer join dept d on e.deptno=d.deptno;
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | dname | loc |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 | 30 | SALES | CHICAGO |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 | 30 | SALES | CHICAGO |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 | 30 | SALES | CHICAGO |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 | 30 | SALES | CHICAGO |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 | 30 | SALES | CHICAGO |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 | 30 | SALES | CHICAGO |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 40 | OPERATIONS | BOSTON |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
15 rows in set (0.00 sec)
*/
全连接
在表关系的笛卡尔积中,出了选择相匹配的数据记录,还包含关联左右两边表中不匹配的数据记录。即全外连接返回两个表中所有匹配和不匹配的行。如果一个表中的一行在另一个表中没有匹配,结果集中也会包含该行,同时在其他表对应的位置填充NULL。
这个在MYSQL中不支持,故不做解释。
自连接
自连接就是指表与其自身进行连接,同样可以看做两张表,只不过两张表一样而已。
案例:
- 查询每个员工对应的领导姓名。
mysql
SELECT e.ename,e.mgr,me.ename '领导' FROM emp e,emp me WHERE e.mgr=me.empno;
/*
mysql> select e.ename, e.mgr, me.ename '领导' from emp e, emp me where e.mgr=me.empno;
+--------+------+-------+
| ename | mgr | 领导 |
+--------+------+-------+
| SMITH | 7902 | FORD |
| ALLEN | 7698 | BLAKE |
| WARD | 7698 | BLAKE |
| JONES | 7839 | KING |
| MARTIN | 7698 | BLAKE |
| BLAKE | 7839 | KING |
| CLARK | 7839 | KING |
| SCOTT | 7566 | JONES |
| TURNER | 7698 | BLAKE |
| ADAMS | 7788 | SCOTT |
| JAMES | 7698 | BLAKE |
| FORD | 7566 | JONES |
| MILLER | 7782 | CLARK |
+--------+------+-------+
13 rows in set (0.00 sec)
*/
七种JOINS实现
上面的交叉连接、内连接、外连接、等值连接 其实都可以从集合角度来看,而从集合角度来看,一共有7中连接方式,如图所示:
内连接
就是上面的内连接。
左连接
上面的左外连接。
右连接
上面的右外连接。
全连接
案例:
- 输出员工和其所在部门的所有信息
- 分析:由于存在有些员工没有部门、有些部门没有员工,之前解决其中一个用的是左外连接和右外连接,但是都是解决其中一个问题的,想要解决全部理论可用全连接 ,但是MYSQL不支持,替代方案:全连接(union),也就是集合运算.
sql
SELECT *
FROM emp e INNER JOIN dept d ON e.deptno = d.deptno;
UNION
SELECT *
FROM emp e INNER JOIN dept d ON e.deptno IS NULL AND d.deptno IS NULL;
/*
mysql> select * from emp e inner join dept d on e.deptno=d.deptno
-> union
-> select * from emp e inner join dept d on e.deptno is null or d.deptno is null;
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | dname | loc |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 | 30 | SALES | CHICAGO |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 | 30 | SALES | CHICAGO |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 | 30 | SALES | CHICAGO |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 | 30 | SALES | CHICAGO |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 | 30 | SALES | CHICAGO |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 | 30 | SALES | CHICAGO |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 | 20 | RESEARCH | DALLAS |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 | 10 | ACCOUNTING | NEW YORK |
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL | 10 | ACCOUNTING | NEW YORK |
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL | 20 | RESEARCH | DALLAS |
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL | 30 | SALES | CHICAGO |
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL | 40 | OPERATIONS | BOSTON |
+-------+--------+-----------+------+------------+------+------+--------+--------+------------+----------+
18 rows in set (0.02 sec)
*/
左排除连接
sql
#左边,左连接,排除d中不包含e中的信息
SELECT *
FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno
WHERE d.deptno IS NULL;
/*
# 我的数据没有哈
mysql> select * from emp e inner join dept d on e.deptno=d.deptno where e.deptno is null;
Empty set (0.00 sec)
*/
右排除连接
sql
SELECT *
FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno
WHERE e.deptno IS NULL;
/*
mysql> SELECT *
-> FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno
-> WHERE e.deptno IS NULL;
+-------+-------+------+------+----------+------+------+--------+--------+------------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno | deptno | dname | loc |
+-------+-------+------+------+----------+------+------+--------+--------+------------+--------+
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 40 | OPERATIONS | BOSTON |
+-------+-------+------+------+----------+------+------+--------+--------+------------+--------+
1 row in set (0.00 sec)
*/
外部排除连接
sql
SELECT *
FROM emp e INNER JOIN dept d ON e.deptno IS NULL AND d.deptno IS NULL;
/*
mysql> SELECT *
-> FROM emp e INNER JOIN dept d ON e.deptno IS NULL AND d.deptno IS NULL;
Empty set (0.00 sec)
*/
集合运算
MySQL支持并集运算。
并集即两个集合所有部分
UNION DISTINCT(union 默认)
- UNION DISTINCT 会删除重复行
- 相同的行在结果中只出现一次。
mysql
SELECT * FROM emp
UNION
SELECT * FROM emp WHERE deptno=10;
/*
mysql> SELECT * FROM emp
-> UNION
-> SELECT * FROM emp WHERE deptno=10;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 |
+-------+--------+-----------+------+------------+------+------+--------+
15 rows in set (0.00 sec)
*/
UNION ALL
- UNION 不会会删除重复行
- 相同的行在结果中可能出现多次
mysql
SELECT * FROM emp
UNION ALL
SELECT * FROM emp WHERE deptno=10;
/*
mysql> select * from emp
-> union all
-> select * from emp where emp.deptno=10;
+-------+--------+-----------+------+------------+------+------+--------+
| empno | ename | job | mgr | hiredate | sal | comm | deptno |
+-------+--------+-----------+------+------------+------+------+--------+
| 6666 | yxz | student | 777 | 2025-01-19 | 800 | 100 | NULL |
| 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 |
| 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600 | 300 | 30 |
| 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250 | 500 | 30 |
| 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975 | NULL | 20 |
| 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250 | 1400 | 30 |
| 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850 | NULL | 30 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 | 3000 | NULL | 20 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500 | 0 | 30 |
| 7876 | ADAMS | CLERK | 7788 | 1987-05-23 | 1100 | NULL | 20 |
| 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950 | NULL | 30 |
| 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000 | NULL | 20 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 |
| 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450 | NULL | 10 |
| 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000 | NULL | 10 |
| 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300 | NULL | 10 |
+-------+--------+-----------+------+------------+------+------+--------+
18 rows in set (0.00 sec)
*/
要求
(1)输入的查询不能包含ORDER BY字句,可以为整个集合运算结果选择性地增加一个ORDER BY字句;
(2)两个查询必须包含相同的列数;
(3)相应列必须具有兼容的数据类型。兼容个的数据类型:优先级较低的数据类型必须能隐式地转换为较高级的数据类型。比如输入的查询1的第一列为int类型,输入的查询2的第一列为float类型,则较低的数据类型int类型可以隐式地转换为较高级float类型。如果输入的查询1的第一列为char类型,输入的查询2的第一列为datetime类型,则会提示转换失败:从字符串转换日期和/或时间时,转换失败;
(4)集合运算结果中列名由输入的查询1决定,如果要为结果分配结果列,应该在输入的查询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单张表即可。
mysql
SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪
FROM emp e
WHERE DATE_FORMAT(e.hiredate,'%Y')='1981' AND e.sal BETWEEN 1500 AND 3500;
/*
mysql> select e.empno, e.ename, e.hiredate, e.sal, (e.sal+ifnull(e.comm, 0))*12
-> from emp e
-> where date_format(e.hiredate, '%Y')='1981' and e.sal between 1500 and 3500;
+-------+--------+------------+------+------------------------------+
| empno | ename | hiredate | sal | (e.sal+ifnull(e.comm, 0))*12 |
+-------+--------+------------+------+------------------------------+
| 7499 | ALLEN | 1981-02-20 | 1600 | 22800 |
| 7566 | JONES | 1981-04-02 | 2975 | 35700 |
| 7698 | BLAKE | 1981-05-01 | 2850 | 34200 |
| 7782 | CLARK | 1981-06-09 | 2450 | 29400 |
| 7844 | TURNER | 1981-09-08 | 1500 | 18000 |
| 7902 | FORD | 1981-12-03 | 3000 | 36000 |
+-------+--------+------------+------+------------------------------+
6 rows in set (0.00 sec)
*/
**步骤二:**加入领导信息,使用自身关联。
mysql
SELECT e.empno,e.ename,e.hiredate,e.sal,(e.sal+IFNULL(e.comm,0))*12 年薪,m.ename 领导
FROM emp e,emp m
WHERE DATE_FORMAT(e.hiredate,'%Y')='1981' AND e.sal BETWEEN 1500 AND 3500
AND e.mgr=m.empno;
/*
mysql> select e.empno, e.ename, e.hiredate, e.sal, (e.sal+ifnull(e.comm, 0))*12, m.ename
-> from emp e, emp m
-> where date_format(e.hiredate, '%Y')='1981' and e.sal between 1500 and 3500
-> and e.mgr=m.empno;
+-------+--------+------------+------+------------------------------+-------+
| empno | ename | hiredate | sal | (e.sal+ifnull(e.comm, 0))*12 | ename |
+-------+--------+------------+------+------------------------------+-------+
| 7499 | ALLEN | 1981-02-20 | 1600 | 22800 | BLAKE |
| 7566 | JONES | 1981-04-02 | 2975 | 35700 | KING |
| 7698 | BLAKE | 1981-05-01 | 2850 | 34200 | KING |
| 7782 | CLARK | 1981-06-09 | 2450 | 29400 | KING |
| 7844 | TURNER | 1981-09-08 | 1500 | 18000 | BLAKE |
| 7902 | FORD | 1981-12-03 | 3000 | 36000 | JONES |
+-------+--------+------------+------+------------------------------+-------+
6 rows in set (0.00 sec)
*/
**步骤三:**加入部门信息。
mysql
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 DATE_FORMAT(e.hiredate,'%Y')='1981' AND e.sal BETWEEN 1500 AND 3500
AND e.mgr=m.empno AND e.deptno=d.deptno;
/*
mysql> select e.empno, e.ename, e.hiredate, e.sal, (e.sal+ifnull(e.comm, 0))*12, m.ename '领导', d.deptno, d.dname, d.lo
c
-> from emp e, emp m, dept d
-> where date_format(e.hiredate, '%Y')='1981' and e.sal between 1500 and 3500
-> and e.mgr=m.empno and e.deptno=d.deptno;
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+
| empno | ename | hiredate | sal | (e.sal+ifnull(e.comm, 0))*12 | 领导 | deptno | dname | loc |
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+
| 7782 | CLARK | 1981-06-09 | 2450 | 29400 | KING | 10 | ACCOUNTING | NEW YORK |
| 7902 | FORD | 1981-12-03 | 3000 | 36000 | JONES | 20 | RESEARCH | DALLAS |
| 7566 | JONES | 1981-04-02 | 2975 | 35700 | KING | 20 | RESEARCH | DALLAS |
| 7844 | TURNER | 1981-09-08 | 1500 | 18000 | BLAKE | 30 | SALES | CHICAGO |
| 7698 | BLAKE | 1981-05-01 | 2850 | 34200 | KING | 30 | SALES | CHICAGO |
| 7499 | ALLEN | 1981-02-20 | 1600 | 22800 | BLAKE | 30 | SALES | CHICAGO |
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+
6 rows in set (0.01 sec)
*/
**步骤四:**加入工资等级
mysql
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 DATE_FORMAT(e.hiredate,'%Y')='1981' AND 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;
/*
mysql> 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 date_format(e.hiredate, '%Y')='1981' and 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;
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+-------+
| empno | ename | hiredate | sal | (e.sal+ifnull(e.comm, 0))*12 | 领导 | deptno | dname | loc | grade |
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+-------+
| 7844 | TURNER | 1981-09-08 | 1500 | 18000 | BLAKE | 30 | SALES | CHICAGO | 3 |
| 7499 | ALLEN | 1981-02-20 | 1600 | 22800 | BLAKE | 30 | SALES | CHICAGO | 3 |
| 7902 | FORD | 1981-12-03 | 3000 | 36000 | JONES | 20 | RESEARCH | DALLAS | 4 |
| 7782 | CLARK | 1981-06-09 | 2450 | 29400 | KING | 10 | ACCOUNTING | NEW YORK | 4 |
| 7698 | BLAKE | 1981-05-01 | 2850 | 34200 | KING | 30 | SALES | CHICAGO | 4 |
| 7566 | JONES | 1981-04-02 | 2975 | 35700 | KING | 20 | RESEARCH | DALLAS | 4 |
+-------+--------+------------+------+------------------------------+-------+--------+------------+----------+-------+
6 rows in set (0.00 sec)
*/
**步骤五:**排序
mysql
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 DATE_FORMAT(e.hiredate,'%Y')='1981' AND 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;
/*
mysql> select e.empno, e.ename, e.hiredate, e.sal, (e.sal+ifnull(e.comm, 0))*12 '年薪', m.ename '领导', d.deptno, d.dnam
e, d.loc, s.grade
-> from emp e, emp m, dept d, salgrade s
-> where date_format(e.hiredate, '%Y')='1981' and 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;
+-------+--------+------------+------+-------+-------+--------+------------+----------+-------+
| empno | ename | hiredate | sal | 年薪 | 领导 | deptno | dname | loc | grade |
+-------+--------+------------+------+-------+-------+--------+------------+----------+-------+
| 7844 | TURNER | 1981-09-08 | 1500 | 18000 | BLAKE | 30 | SALES | CHICAGO | 3 |
| 7499 | ALLEN | 1981-02-20 | 1600 | 22800 | BLAKE | 30 | SALES | CHICAGO | 3 |
| 7902 | FORD | 1981-12-03 | 3000 | 36000 | JONES | 20 | RESEARCH | DALLAS | 4 |
| 7782 | CLARK | 1981-06-09 | 2450 | 29400 | KING | 10 | ACCOUNTING | NEW YORK | 4 |
| 7698 | BLAKE | 1981-05-01 | 2850 | 34200 | KING | 30 | SALES | CHICAGO | 4 |
| 7566 | JONES | 1981-04-02 | 2975 | 35700 | KING | 20 | RESEARCH | DALLAS | 4 |
+-------+--------+------------+------+-------+-------+--------+------------+----------+-------+
6 rows in set (0.00 sec)
*/