MYSQL学习笔记(四):多表关系、多表查询(交叉连接、内连接、外连接、自连接)、七种JSONS、集合

前言

  • 学习和使用数据库可以说是程序员必须具备能力,这里将更新关于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 默认)

  1. UNION DISTINCT 会删除重复行
  2. 相同的行在结果中只出现一次。
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

  1. UNION 不会会删除重复行
  2. 相同的行在结果中可能出现多次
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相等

多表查询练习

  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单张表即可。

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)
*/
相关推荐
玛丽莲茼蒿5 小时前
Linux/Unix学习笔记(四)—— 进程管理
linux·学习·unix
这个DBA有点耶6 小时前
DBA的AI助手:向量检索与NL2SQL入门
数据库·人工智能·postgresql·学习方法·dba
㳺三才人子6 小时前
初探 Flask
后端·python·flask·html
星栈独行6 小时前
我在 Rust 全栈项目里用 JWT 做无状态认证
开发语言·后端·rust·前端框架·开源·github·web
richxu202510016 小时前
学完了江科大STM32,下一步该怎么学?
stm32·单片机·嵌入式硬件·学习
Java爱好狂.6 小时前
Java程序员体系化学习路线(2026最新版)
java·后端·java面试·java架构师·java程序员·java八股文·java学习路线
网络与设备以及操作系统学习使用者6 小时前
Linux与Windows核心差异深度解析
linux·运维·网络·windows·学习
陈随易6 小时前
Redis 8.8发布,一定要更新
前端·后端·程序员
basketball6167 小时前
SQL 常用数据格式化操作方法总结
数据库·sql
Leo⁵7 小时前
基于 Git 的 Obsidian 笔记同步
笔记·git