先给大家抛一个结论,MYSQL中一切皆表格
前面我们讲解的mysql表的查询都是对一张表进行查询,在实际开发中这远远不够。
注意:这篇文章的相关指令我进行了补充,所以目录里的内容不代表全部,有的我没有用目录表示,但文章内容很齐全,内容很详细,方便大家学习
一. 基本查询回顾
1.查询工资高于500或岗位为MANAGER的雇员

2.查询工资高于500或岗位为MANAGER的雇员,同时还要满足他们的姓名首字母为大写的J

3.按照部门号升序而雇员的工资降序排序

4.按部门编号升序、同一部门内按工资降序,查询员工的姓名、工资和部门编号。

5.使用年薪进行降序排序

计算每位员工的"年薪"(月薪 × 12 + 年度奖金),其中奖金为 NULL 的按 0 计算。

使用年薪进行降序排序

6.显示工资最高的员工的名字和工作岗位
- 表中薪资最高的

- 薪资为5000的人员信息

- 显示工资最高的员工的名字和工作岗位
这个显示的是全部信息

显示的名字和工作岗位

7.显示工资高于平均工资的员工信息
查询平均工资

显示工资高于平均工资的员工信息

8.显示每个部门的平均工资和最高工资
显示每个部门的平均工资和最高工资

带部门编号

这里进行了格式化处理fromat(),保留了两位小数

在 MySQL 中的两种别名写法
MySQL 支持两种别名写法,效果完全一样:
写法1:不用 AS
SELECT max(sal) 最高 FROM EMP;写法2:用 AS(更规范/标准)
SELECT max(sal) AS 最高 FROM EMP;两种都正确,MySQL 都认。
9.显示平均工资高于2000的部门号和它的平均工资
显示平均工资高于2000的部门号和它的平均工资

显示平均工资不高于2000的部门号和它的平均工资

10.显示每种岗位的雇员总数,平均工资
显示岗位雇员总数量

显示每个部门工作岗位的人数,并对这些岗位的人数进行升序排序

二.多表查询
**实际开发中往往数据来自不同的表,所以需要多表查询。本节我们用一个简单的公司管理系统,有三张表EMP,DEPT,SALGRADE来演示如何进行多表查询。**将数据进行穷举组合 ------ 笛卡尔积)

进行练习:
笛卡尔积(Cartesian Product)
1. 什么是笛卡尔积?
将两张表所有行进行无条件组合,左表的每一行与右表的每一行都匹配一次。
2. 产过程(你原来的描述,优化后保留)
-
从第一张表中取出第一条记录,与第二张表的所有记录依次组合;
-
再从第一张表中取出第二条记录,同样与第二张表的所有记录组合;
-
依次类推,直到第一张表的所有记录都组合完毕。
如果不加任何过滤条件,得到的结果就是笛卡尔积。
3. 笛卡尔积的行数公式
结果行数 = 表1行数 × 表2行数
示例:
EMP 表:14 行
DEPT 表:4 行
笛卡尔积 = 14 × 4 = 56 行

4. 使用笛卡尔积的典型问题
-
大部分组合是没有实际意义的(例如:SMITH 与 部门 40 匹配,但他并不在部门40)
-
会产生大量冗余数据,严重影响性能
5. 如何解决:加过滤条件(连接条件)
-- 错误:笛卡尔积(无意义)
SELECT * FROM EMP, DEPT;
-- 正确:只保留部门编号相同的有效组合
SELECT * FROM EMP, DEPT
WHERE EMP.DEPTNO = DEPT.DEPTNO;
这也是 等值连接 的起源。
补充:
笛卡尔积是连接查询的"原始素材",必须通过连接条件(如 WHERE)过滤出有意义的行,才能得到正确的查询结果。
**显示雇员名、雇员工资以及所在部门的名字因为上面的数据来自EMP和DEPT表,因此要联合查(**将数据进行穷举组合 ------ 笛卡尔积)
其实我们只要emp表中的deptno = dept表中的deptno字段的记录

显示部门号为10的部门名,员工名和工资
显示的全部信息

显示部门号为10的部门名,员工名和工资

显示部门号为10的部门名,员工名,工资和部门号

显示各个员工的姓名,工资,及工资级别
查询 EMP 表和 SALGRADE 表的所有列,进行无条件组合,得到两张表的笛卡尔积

查询所有员工的姓名、工资以及可能对应的工资等级

显示各个员工的姓名,工资,及工资级别(后面有where的筛选条件)

补充hisal和losal的字段

LOSAL= 该等级的最低工资(Low Salary)
HISAL= 该等级的最高工资(High Salary)两者共同定义了一个工资区间,用来判断员工属于哪个工资等级。
EMP.deptno 这个写法的三个作用
| 作用 | 说明 |
|---|---|
| 消除歧义 | 告诉数据库"我指的是 EMP 表的 deptno" |
| 提高可读性 | 别人看 SQL 就知道这个字段来自哪张表 |
| 避免错误 | 防止因字段名重复导致的 ambiguous 错误 |
三.自连接
自连接是指在同一张表连接查询
这是同一张表(SALGRADE),只是起了两个不同的别名(t1 和 t2),然后对自己做笛卡尔积。 并不是两张不同的表,而是一张表自己跟自己连接 ,这种操作叫做自连接(Self Join)。

显示员工FORD的上级领导的编号和姓名(mgr是员工领导的编号--empno)
使用的子查询:

使用多表查询(自查询)
- 使用到表的别名
- from emp e1, emp e2,给自己的表起别名,因为要先做笛卡尔积,所以别名可以先识别



FROM EMP e1, EMP e2对同一张EMP表起两个不同的别名:e1(左表)、e2(右表)数据库先根据别名,将一张表逻辑上视为两张独立的表
然后对
e1和e2做笛卡尔积(e1 的每一行 × e2 的每一行)最后通过
WHERE条件过滤出符合条件的行
| 步骤 | 说明 |
|---|---|
| ① | 把 EMP 表当作两个独立角色:e1(下属表)、e2(上级表) |
| ② | 先做笛卡尔积:每个"下属"与所有"上级"组合 |
| ③ | 过滤出 e1.ename = 'FORD'(下属是 FORD) |
| ④ | 再过滤 e1.mgr = e2.empno(FORD 的上级编号 = 上级表的员工编号) |
| ⑤ | 最终输出:FORD 的上级的姓名和编号 |
别名的作用:
没有别名时,数据库无法区分"谁是上级""谁是下属";有了别名,才能写
e1.mgr = e2.empno
别名的原因:(重点补充)
自连接的核心问题 :
同一张表在 SQL 语句中出现两次,如果不加别名,数据库不知道
mgr和empno的对比是在同一行内还是跨行。
4.子查询
子查询:SQL 中嵌套的 SELECT 语句,也叫嵌套查询。
常见位置:WHERE / SELECT / FROM / HAVING / EXISTS
核心理解:子查询的结果,在逻辑上始终是一张临时表(即使是单值,也是一行一列的表)。
执行顺序:子查询先执行,结果再交给外层查询使用。
当前学习重点 :子查询在 WHERE 子句中作为判断条件(如
IN、=、>等)。
(1)单行子查询
返回一行记录的子查询
单行子查询 :子查询返回一行记录 (可以是一列或多列),外层查询使用单行比较运算符(如
=、>、<、>=、<=、<>)与子查询结果进行比较。
显示SMITH同一部门的员工
分步理解查询

一步到位查询:

(2)多行子查询
返回多行记录的子查询
多行子查询返回多行记录,必须配合
IN、ANY、ALL等运算符使用,不能直接用=
in关键字
in 关键字用来判断一个对应的列值是否在某个集合当中,只要在集合当中,表明说查找成功。
查询与 10 号部门员工 (CLERK、MANAGER、PRESIDENT)拥有相同职位,但自己不在 10 号部门的员工信息。
sql
SELECT ename, job, sal, deptno
FROM EMP
WHERE job IN (
SELECT DISTINCT job
FROM EMP
WHERE deptno = 10
)
AND deptno <> 10;

先通过子查询找出"职位与 10 号部门相同但自己不在 10 号部门"的员工,再关联部门表查出他们所在的部门名称。

执行顺序:最内层(10号部门的职位)→ 中间层(符合条件且不在10号的员工)→ 外层(关联部门表得到部门名称)。
SQL 是三层嵌套结构 ,执行顺序严格遵循 由最内层向外层依次执行 的原则
这样执行的原因是:外层查询依赖于内层查询的结果。
第 1 步(最内层) :
SELECT ... FROM EMP WHERE deptno = 10
- 它不依赖外面的任何查询。它自己就能独立运行,得到一个结果集(10号部门的职位列表)。
第 2 步(中间层) :
SELECT ... FROM EMP WHERE job IN ( ... )
这里的
( ... )代表第 1 步的结果。如果第 1 步没执行完,数据库就不知道
IN里面应该有哪些职位,第 2 步就无法执行。第 3 步(最外层) :
SELECT ... FROM tmp, DEPT ...
这里的
tmp代表第 2 步的结果。如果第 2 步没执行完,数据库就不知道
tmp这张临时表里有什么数据,第 3 步就无法执行。
all关键字
all 关键字表示与所有的值作比较。
sql
select ename, sal, deptno from EMP where sal > all(select sal from EMP where
deptno=30);
部门30的最高工资:

显示工资比部门30的所有员工的工资高的员工的姓名、工资和部门号

另一种写法:

any关键字
any 关键字表示与任意的值作比较。
显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门
的员工)
查询出工资大于 30 号部门中"任意一个"员工工资的员工。

显示工资比部门30的任意员工的工资高的员工的姓名、工资和部门号(包含自己部门
的员工)

(3)多列子查询
单行子查询是指子查询只返回单列,单行数据;
多行子查询是指返回单列多行数据,都是针对单列而言的,而多列子查询则是指查询返回多个列数据的子查询语句
查询和SMITH的部门和岗位完全相同的所有雇员的员工信息,不含SMITH本人

子查询返回了两列(DEPTNO 和 JOB),外层用
(列1, 列2) = (子查询)进行比较,找出与 SMITH 部门相同、职位相同的其他员工
(4)在from子句中使用子查询
当子查询出现在
FROM子句中时,它被当作一张临时的"派生表"(Derived Table)来使用。技巧本质:把子查询的查询结果,在逻辑上视为一张临时存在的表,外层查询可以像操作普通表一样操作它。
练习:显示每个高于自己部门平均工资的员工的姓名、部门、工资、平均工资
获取各个部门的平均工资,将其看作临时表

扩展:显示他们的所在部门的城市位置和部门编号。

查找每个部门工资最高的人的员工信息

查找每个部门工资最高的人的姓名、工资、部门、最高工资

显示每个部门的信息(部门名,编号,地址)和人员数量
方法1:使用多表

多表查询的本质指导思想
核心目标:将多表问题转化为单表问题。
核心方法:
通过 连接条件(JOIN / WHERE) 将多张表合并成一张逻辑上的"大宽表"
或通过 子查询 先构建临时结果集
后续操作 :一旦临时表形成,后续的
SELECT、WHERE、GROUP BY、ORDER BY等操作,全部与单表查询完全一致。本质理解 :无论多表查询多复杂,最终都可以理解为先构建一张临时单表,然后对它做标准查询。
方法2:使用子查询
1. 对EMP表进行人员统计

2. 将上面的表看作临时表

5.合并查询
在实际应用中,为了合并多个select的执行结果,可以使用集合操作符 union ,union all
union(已经去重)
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
将工资大于25000或职位是MANAGER的人找出来

union all(没有去重)
该操作符用于取得两个结果集的并集。当使用该操作符时,不会去掉结果集中的重复行。
将工资大于 2500 或职位是 MANAGER 的人找出来

UNION/UNION ALL使用前提
列数必须相同
对应列的数据类型兼容
列的顺序按位置匹配(不是按列名)
最终列名以第一个 SELECT 为准
6.单行 ,多行 ,多列 子查询区别
| 对比维度 | 单行子查询 | 多行子查询 | 多列子查询 |
|---|---|---|---|
| 划分依据 | 按返回行数 | 按返回行数 | 按返回列数 |
| 返回特点 | 返回 1 行 | 返回 多行(0~n行) | 返回 多列(≥2列) |
| 允许返回列数 | 1列或多列 | 1列或多列 | 2列或以上 |
| 使用运算符 | = > < >= <= <> |
IN ANY ALL EXISTS |
= 或 IN(与行数配合) |
| 左侧写法 | WHERE 列 或 WHERE (列1,列2) |
WHERE 列 或 WHERE (列1,列2) |
WHERE (列1,列2,...) |
| 常见错误 | 子查询返回多行会报错 | NOT IN 遇 NULL 结果为空 |
左侧列数与子查询列数不匹配 |
| 示例 | WHERE sal > (SELECT AVG(sal) FROM EMP) |
WHERE sal IN (SELECT sal FROM EMP WHERE deptno=30) |
WHERE (deptno,job) = (SELECT deptno,job FROM EMP WHERE ename='SMITH') |