文章目录
-
- 复合查询:多表数据的整合
- 一、前言
- 二、准备测试数据
-
- [2.1 创建测试表](#2.1 创建测试表)
- 三、多表查询基础
-
- [3.1 笛卡尔积](#3.1 笛卡尔积)
- [3.2 等值连接](#3.2 等值连接)
- [3.3 表别名](#3.3 表别名)
- [3.4 三表连接](#3.4 三表连接)
- 四、自连接
-
- [4.1 自连接的概念](#4.1 自连接的概念)
- [4.2 自连接示例](#4.2 自连接示例)
- 五、子查询
-
- [5.1 子查询的概念](#5.1 子查询的概念)
- [5.2 单行子查询](#5.2 单行子查询)
-
- 示例:查询工资最高的员工
- 示例:查询工资高于平均工资的员工
- [示例:查询与 SMITH 同部门的员工](#示例:查询与 SMITH 同部门的员工)
- [5.3 多行子查询](#5.3 多行子查询)
-
- [IN 操作符](#IN 操作符)
- [ALL 操作符](#ALL 操作符)
- [ANY 操作符](#ANY 操作符)
- [5.4 多列子查询](#5.4 多列子查询)
-
- [示例:查询与 SMITH 部门和岗位完全相同的员工](#示例:查询与 SMITH 部门和岗位完全相同的员工)
- [5.5 FROM 子句中的子查询](#5.5 FROM 子句中的子查询)
- [六、UNION 合并查询](#六、UNION 合并查询)
-
- [6.1 UNION 的概念](#6.1 UNION 的概念)
- [6.2 UNION 语法](#6.2 UNION 语法)
- [6.3 UNION 示例](#6.3 UNION 示例)
-
- [查询工资 > 2500 或职位是 MANAGER 的员工](#查询工资 > 2500 或职位是 MANAGER 的员工)
- [6.4 UNION ALL](#6.4 UNION ALL)
- [6.5 UNION vs OR](#6.5 UNION vs OR)
- 七、复合查询的最佳实践
-
- [7.1 查询优化建议](#7.1 查询优化建议)
- [7.2 常见错误](#7.2 常见错误)
- [7.3 性能考虑](#7.3 性能考虑)
- 八、完整的复合查询示例
-
- [8.1 综合查询案例](#8.1 综合查询案例)
- [8.2 多层子查询案例](#8.2 多层子查询案例)
- [8.3 复杂的 UNION 案例](#8.3 复杂的 UNION 案例)
- 九、总结与下一步
-
- [10.1 复合查询的核心要点](#10.1 复合查询的核心要点)
- [10.2 何时使用哪种方法](#10.2 何时使用哪种方法)
- [10.3 建议练习](#10.3 建议练习)
复合查询:多表数据的整合
一、前言
💬 这一篇讲什么:复杂的多表查询
🚀 核心内容:
- 如何从多个表中查询数据?
- 什么是自连接?
- 如何使用子查询?
- 如何合并多个查询结果?
在前面的学习中,我们的查询都是基于单个表。但在实际开发中,数据往往分散在多个表中,需要通过关联查询来获取完整的信息。这一篇讲解复合查询的各种技术,是 MySQL 学习中的重点。
二、准备测试数据
2.1 创建测试表
为了演示复合查询,我们使用经典的公司管理系统数据模型,包含三张表。
创建部门表:
sql
CREATE TABLE dept (
deptno INT PRIMARY KEY,
dname VARCHAR(20),
loc VARCHAR(20)
);
INSERT INTO dept VALUES
(10, '财务部', '北京'),
(20, '研发部', '上海'),
(30, '销售部', '深圳'),
(40, '行政部', '广州');
创建员工表:
sql
CREATE TABLE emp (
empno INT PRIMARY KEY,
ename VARCHAR(20),
job VARCHAR(20),
mgr INT,
hiredate DATE,
sal DECIMAL(10, 2),
comm DECIMAL(10, 2),
deptno INT
);
INSERT INTO emp VALUES
(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);
创建工资等级表:
sql
CREATE TABLE salgrade (
grade INT,
losal DECIMAL(10, 2),
hisal DECIMAL(10, 2)
);
INSERT INTO salgrade VALUES
(1, 700, 1200),
(2, 1201, 1400),
(3, 1401, 2000),
(4, 2001, 3000),
(5, 3001, 9999);
三、多表查询基础
3.1 笛卡尔积
当从多个表中查询数据时,如果不指定连接条件,会产生笛卡尔积(所有行的组合)。
笛卡尔积示例
sql
-- 不指定连接条件,产生笛卡尔积
SELECT COUNT(*) FROM emp, dept;
输出:
bash
+----------+
| COUNT(*) |
+----------+
| 56 | -- 14 个员工 × 4 个部门 = 56
+----------+
这不是我们想要的结果。我们需要指定连接条件。
3.2 等值连接
通过指定连接条件(通常是外键关系),从多个表中查询相关数据。
基本语法
sql
SELECT column_list
FROM table1, table2
WHERE table1.key = table2.key;
示例:查询员工名、工资和部门名
sql
SELECT emp.ename, emp.sal, dept.dname
FROM emp, dept
WHERE emp.deptno = dept.deptno;
输出:
bash
+--------+--------+--------+
| ename | sal | dname |
+--------+--------+--------+
| SMITH | 800.00 | 研发部 |
| ALLEN | 1600.00| 销售部 |
| WARD | 1250.00| 销售部 |
| JONES | 2975.00| 研发部 |
| MARTIN | 1250.00| 销售部 |
| BLAKE | 2850.00| 销售部 |
| CLARK | 2450.00| 财务部 |
| SCOTT | 3000.00| 研发部 |
| KING | 5000.00| 财务部 |
| TURNER | 1500.00| 销售部 |
| ADAMS | 1100.00| 研发部 |
| JAMES | 950.00 | 销售部 |
| FORD | 3000.00| 研发部 |
| MILLER | 1300.00| 财务部 |
+--------+--------+--------+
3.3 表别名
为了简化查询,可以给表起别名。
sql
SELECT e.ename, e.sal, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;
说明 :e 是 emp 的别名,d 是 dept 的别名。
3.4 三表连接
sql
-- 查询员工名、工资、部门名和工资等级
SELECT e.ename, e.sal, d.dname, s.grade
FROM emp e, dept d, salgrade s
WHERE e.deptno = d.deptno
AND e.sal BETWEEN s.losal AND s.hisal;
输出:
bash
+--------+--------+--------+-------+
| ename | sal | dname | grade |
+--------+--------+--------+-------+
| SMITH | 800.00 | 研发部 | 1 |
| ALLEN | 1600.00| 销售部 | 3 |
| WARD | 1250.00| 销售部 | 2 |
| JONES | 2975.00| 研发部 | 4 |
| MARTIN | 1250.00| 销售部 | 2 |
| BLAKE | 2850.00| 销售部 | 4 |
| CLARK | 2450.00| 财务部 | 4 |
| SCOTT | 3000.00| 研发部 | 5 |
| KING | 5000.00| 财务部 | 5 |
| TURNER | 1500.00| 销售部 | 3 |
| ADAMS | 1100.00| 研发部 | 1 |
| JAMES | 950.00 | 销售部 | 1 |
| FORD | 3000.00| 研发部 | 5 |
| MILLER | 1300.00| 财务部 | 2 |
+--------+--------+--------+-------+
四、自连接
4.1 自连接的概念
自连接 是指同一张表与自己进行连接。在 emp 表中,mgr 字段存储的是员工的上级领导的员工编号,这形成了一个层级关系。
4.2 自连接示例
查询员工及其上级领导的信息
需求:显示员工 FORD 的上级领导的编号和姓名。
方式一:使用子查询:
sql
SELECT empno, ename
FROM emp
WHERE empno = (SELECT mgr FROM emp WHERE ename = 'FORD');
输出:
bash
+-------+-------+
| empno | ename |
+-------+-------+
| 7566 | JONES |
+-------+-------+
方式二:使用自连接(推荐):
sql
-- 给同一张表起两个别名:leader(领导)和 worker(员工)
SELECT leader.empno, leader.ename
FROM emp leader, emp worker
WHERE leader.empno = worker.mgr
AND worker.ename = 'FORD';
输出相同。
查询所有员工及其上级领导
sql
SELECT
worker.ename AS 员工,
worker.sal AS 工资,
leader.ename AS 上级领导,
leader.sal AS 领导工资
FROM emp worker, emp leader
WHERE worker.mgr = leader.empno;
输出:
bash
+--------+--------+--------+--------+
| 员工 | 工资 | 上级领导| 领导工资|
+--------+--------+--------+--------+
| SMITH | 800.00 | FORD | 3000.00|
| ALLEN | 1600.00| BLAKE | 2850.00|
| WARD | 1250.00| BLAKE | 2850.00|
| JONES | 2975.00| KING | 5000.00|
| MARTIN | 1250.00| BLAKE | 2850.00|
| BLAKE | 2850.00| KING | 5000.00|
| CLARK | 2450.00| KING | 5000.00|
| SCOTT | 3000.00| JONES | 2975.00|
| TURNER | 1500.00| BLAKE | 2850.00|
| ADAMS | 1100.00| SCOTT | 3000.00|
| JAMES | 950.00 | BLAKE | 2850.00|
| FORD | 3000.00| JONES | 2975.00|
| MILLER | 1300.00| CLARK | 2450.00|
+--------+--------+--------+--------+
说明:KING 是总裁,没有上级,所以不在结果中。
五、子查询
5.1 子查询的概念
子查询是指嵌入在其他 SQL 语句中的 SELECT 语句,也叫嵌套查询。子查询可以出现在 SELECT、FROM、WHERE 等子句中。
5.2 单行子查询
子查询返回单行单列的结果。
示例:查询工资最高的员工
sql
SELECT ename, job
FROM emp
WHERE sal = (SELECT MAX(sal) FROM emp);
输出:
bash
+-------+-----------+
| ename | job |
+-------+-----------+
| KING | PRESIDENT |
+-------+-----------+
示例:查询工资高于平均工资的员工
sql
SELECT ename, sal
FROM emp
WHERE sal > (SELECT AVG(sal) FROM emp);
输出:
bash
+-------+--------+
| ename | sal |
+-------+--------+
| JONES | 2975.00|
| BLAKE | 2850.00|
| SCOTT | 3000.00|
| KING | 5000.00|
| FORD | 3000.00|
+-------+--------+
示例:查询与 SMITH 同部门的员工
sql
SELECT *
FROM emp
WHERE deptno = (SELECT deptno FROM emp WHERE ename = 'SMITH');
输出:SMITH 所在部门(20 - 研发部)的所有员工。
5.3 多行子查询
子查询返回多行单列的结果。需要使用 IN、ALL、ANY 等操作符。
IN 操作符
语法:
sql
WHERE column IN (subquery)
示例:查询与 10 号部门工作岗位相同的员工,但不包括 10 号部门的员工。
sql
SELECT ename, job, sal, deptno
FROM emp
WHERE job IN (SELECT DISTINCT job FROM emp WHERE deptno = 10)
AND deptno <> 10;
输出:
bash
+--------+----------+--------+--------+
| ename | job | sal | deptno |
+--------+----------+--------+--------+
| SMITH | CLERK | 800.00 | 20 |
| ADAMS | CLERK | 1100.00| 20 |
| JAMES | CLERK | 950.00 | 30 |
| MILLER | CLERK | 1300.00| 10 |
+--------+----------+--------+--------+
ALL 操作符
语法:
sql
WHERE column > ALL (subquery) -- 大于所有值
WHERE column < ALL (subquery) -- 小于所有值
示例:显示工资比部门 30 的所有员工工资都高的员工。
sql
SELECT ename, sal, deptno
FROM emp
WHERE sal > ALL (SELECT sal FROM emp WHERE deptno = 30);
输出:
bash
+-------+--------+--------+
| ename | sal | deptno |
+-------+--------+--------+
| JONES | 2975.00| 20 |
| SCOTT | 3000.00| 20 |
| KING | 5000.00| 10 |
| FORD | 3000.00| 20 |
+-------+--------+--------+
说明:部门 30 的最高工资是 BLAKE 的 2850,所以工资 > 2850 的员工都被选中。
ANY 操作符
语法:
sql
WHERE column > ANY (subquery) -- 大于任意一个值
WHERE column < ANY (subquery) -- 小于任意一个值
示例:显示工资比部门 30 的任意员工工资高的员工。
sql
SELECT ename, sal, deptno
FROM emp
WHERE sal > ANY (SELECT sal FROM emp WHERE deptno = 30);
输出:
bash
+--------+--------+--------+
| ename | sal | deptno |
+--------+--------+--------+
| ALLEN | 1600.00| 30 |
| WARD | 1250.00| 30 |
| JONES | 2975.00| 20 |
| MARTIN | 1250.00| 30 |
| BLAKE | 2850.00| 30 |
| CLARK | 2450.00| 10 |
| SCOTT | 3000.00| 20 |
| KING | 5000.00| 10 |
| TURNER | 1500.00| 30 |
| ADAMS | 1100.00| 20 |
| FORD | 3000.00| 20 |
| MILLER | 1300.00| 10 |
+--------+--------+--------+
说明:只要工资大于部门 30 中的任意一个员工(最低 950),就被选中。
5.4 多列子查询
子查询返回多列数据。
示例:查询与 SMITH 部门和岗位完全相同的员工
sql
SELECT ename
FROM emp
WHERE (deptno, job) = (SELECT deptno, job FROM emp WHERE ename = 'SMITH')
AND ename <> 'SMITH';
输出:
bash
+-------+
| ename |
+-------+
| ADAMS |
+-------+
说明:SMITH 在部门 20,岗位是 CLERK。ADAMS 也在部门 20,岗位也是 CLERK。
5.5 FROM 子句中的子查询
把子查询的结果当作一个临时表使用。
示例:显示高于自己部门平均工资的员工
sql
SELECT e.ename, e.deptno, e.sal, FORMAT(tmp.avg_sal, 2) AS 部门平均工资
FROM emp e,
(SELECT AVG(sal) avg_sal, deptno FROM emp GROUP BY deptno) tmp
WHERE e.sal > tmp.avg_sal
AND e.deptno = tmp.deptno;
输出:
bash
+-------+--------+--------+--------+
| ename | deptno | sal | 部门平均工资|
+-------+--------+--------+--------+
| JONES | 20 | 2975.00| 1975.00|
| BLAKE | 30 | 2850.00| 1566.67|
| CLARK | 10 | 2450.00| 2400.00|
| SCOTT | 20 | 3000.00| 1975.00|
| KING | 10 | 5000.00| 2400.00|
| FORD | 20 | 3000.00| 1975.00|
+-------+--------+--------+--------+
示例:查找每个部门工资最高的员工
sql
SELECT e.ename, e.sal, e.deptno, tmp.max_sal
FROM emp e,
(SELECT MAX(sal) max_sal, deptno FROM emp GROUP BY deptno) tmp
WHERE e.deptno = tmp.deptno
AND e.sal = tmp.max_sal;
输出:
bash
+-------+--------+--------+--------+
| ename | sal | deptno | max_sal|
+-------+--------+--------+--------+
| CLARK | 2450.00| 10 | 2450.00|
| KING | 5000.00| 10 | 5000.00|
| SCOTT | 3000.00| 20 | 3000.00|
| FORD | 3000.00| 20 | 3000.00|
| BLAKE | 2850.00| 30 | 2850.00|
+-------+--------+--------+--------+
示例:显示每个部门的信息和人员数量
方式一:多表连接:
sql
SELECT d.dname, d.deptno, d.loc, COUNT(*) AS 部门人数
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY d.deptno, d.dname, d.loc;
方式二:FROM 子句中的子查询:
sql
SELECT d.deptno, d.dname, d.loc, tmp.cnt
FROM dept d,
(SELECT COUNT(*) cnt, deptno FROM emp GROUP BY deptno) tmp
WHERE d.deptno = tmp.deptno;
输出:
bash
+--------+--------+------+------+
| deptno | dname | loc | cnt |
+--------+--------+------+------+
| 10 | 财务部 | 北京 | 3 |
| 20 | 研发部 | 上海 | 5 |
| 30 | 销售部 | 深圳 | 6 |
+--------+--------+------+------+
六、UNION 合并查询
6.1 UNION 的概念
UNION 用于合并两个或多个 SELECT 语句的结果集。UNION 会自动去除重复行。
6.2 UNION 语法
sql
SELECT column_list FROM table1
UNION
SELECT column_list FROM table2;
要求:
- 两个 SELECT 语句的列数必须相同。
- 对应列的数据类型应该兼容。
- 列名以第一个 SELECT 为准。
6.3 UNION 示例
查询工资 > 2500 或职位是 MANAGER 的员工
方式一:使用 OR:
sql
SELECT ename, sal, job
FROM emp
WHERE sal > 2500 OR job = 'MANAGER';
方式二:使用 UNION:
sql
SELECT ename, sal, job
FROM emp
WHERE sal > 2500
UNION
SELECT ename, sal, job
FROM emp
WHERE job = 'MANAGER';
输出(UNION 去除重复):
bash
+-------+--------+-----------+
| ename | sal | job |
+-------+--------+-----------+
| JONES | 2975.00| MANAGER |
| BLAKE | 2850.00| MANAGER |
| SCOTT | 3000.00| ANALYST |
| KING | 5000.00| PRESIDENT |
| FORD | 3000.00| ANALYST |
| CLARK | 2450.00| MANAGER |
+-------+--------+-----------+
说明:JONES、BLAKE、CLARK 既满足工资 > 2500,也是 MANAGER,但只显示一次。
6.4 UNION ALL
UNION ALL 不去除重复行。
sql
SELECT ename, sal, job
FROM emp
WHERE sal > 2500
UNION ALL
SELECT ename, sal, job
FROM emp
WHERE job = 'MANAGER';
输出(包含重复):
bash
+-------+--------+-----------+
| ename | sal | job |
+-------+--------+-----------+
| JONES | 2975.00| MANAGER |
| BLAKE | 2850.00| MANAGER |
| SCOTT | 3000.00| ANALYST |
| KING | 5000.00| PRESIDENT |
| FORD | 3000.00| ANALYST |
| JONES | 2975.00| MANAGER | -- 重复
| BLAKE | 2850.00| MANAGER | -- 重复
| CLARK | 2450.00| MANAGER |
+-------+--------+-----------+
6.5 UNION vs OR
| 特性 | UNION | OR |
|---|---|---|
| 去重 | 自动去重 | 不去重 |
| 性能 | 可能较慢(需要排序) | 通常较快 |
| 可读性 | 复杂条件时更清晰 | 简单条件时更简洁 |
| 使用场景 | 来自不同表或复杂条件 | 同一表的简单条件 |
七、复合查询的最佳实践
7.1 查询优化建议
多表连接 vs 子查询:
| 方式 | 优点 | 缺点 |
|---|---|---|
| 多表连接 | 性能通常更好 | 代码可能复杂 |
| 子查询 | 逻辑清晰 | 性能可能较差 |
建议:优先使用多表连接,除非子查询能显著提高可读性。
7.2 常见错误
❌ 错误一:GROUP BY 中缺少列(所有普通列必须出现在 GROUP BY 里)
sql
-- 错误:SELECT 中的 dname 没有在 GROUP BY 中
SELECT d.dname, COUNT(*)
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY e.deptno;
-- 正确
SELECT d.dname, COUNT(*)
FROM emp e, dept d
WHERE e.deptno = d.deptno
GROUP BY e.deptno, d.dname;
❌ 错误二:子查询返回多行但用 = 比较
sql
-- 错误:子查询返回多行,不能用 =
SELECT * FROM emp WHERE deptno = (SELECT deptno FROM emp WHERE sal > 2000);
-- 正确:使用 IN
SELECT * FROM emp WHERE deptno IN (SELECT deptno FROM emp WHERE sal > 2000);
❌ 错误三:自连接时忘记表别名
sql
-- 错误:无法区分两个表
SELECT * FROM emp, emp WHERE emp.mgr = emp.empno;
-- 正确
SELECT * FROM emp e1, emp e2 WHERE e1.mgr = e2.empno;
7.3 性能考虑
- 避免在 WHERE 中使用函数:可能导致索引失效。
- 合理使用 UNION:UNION 需要排序,性能不如 OR。
- 子查询优化:尽量在子查询中过滤数据,减少临时表的大小。
- 慎用 DISTINCT:DISTINCT 需要排序,性能较差。
八、完整的复合查询示例
8.1 综合查询案例
需求:显示所有高于自己部门平均工资的员工信息,包括员工名、工资、部门名、部门平均工资,并按部门升序、工资降序排列。
sql
SELECT
e.ename AS 员工名,
e.sal AS 工资,
d.dname AS 部门名,
FORMAT(tmp.avg_sal, 2) AS 部门平均工资,
(e.sal - tmp.avg_sal) AS 超出金额
FROM emp e
INNER JOIN dept d ON e.deptno = d.deptno
INNER JOIN (SELECT AVG(sal) avg_sal, deptno FROM emp GROUP BY deptno) tmp
ON e.deptno = tmp.deptno
WHERE e.sal > tmp.avg_sal
ORDER BY d.deptno ASC, e.sal DESC;
输出:
bash
+------+--------+--------+--------+------+
| 员工名| 工资 | 部门名 | 部门平均工资| 超出金额|
+------+--------+--------+--------+------+
| KING | 5000.00| 财务部 | 2400.00| 2600.00|
| CLARK| 2450.00| 财务部 | 2400.00| 50.00 |
| FORD | 3000.00| 研发部 | 1975.00| 1025.00|
| SCOTT| 3000.00| 研发部 | 1975.00| 1025.00|
| JONES| 2975.00| 研发部 | 1975.00| 1000.00|
| BLAKE| 2850.00| 销售部 | 1566.67| 1283.33|
+------+--------+--------+--------+------+
8.2 多层子查询案例
需求:找出工资高于所有 CLERK 的员工信息。
sql
SELECT ename, sal, job
FROM emp
WHERE sal > (
SELECT MAX(sal)
FROM emp
WHERE job = 'CLERK'
);
输出:所有工资高于最高的 CLERK 工资的员工。
8.3 复杂的 UNION 案例
需求:合并显示所有经理的信息和所有工资高于 3000 的员工信息。
sql
SELECT empno, ename, sal, job, deptno, 'MANAGER' AS 标签
FROM emp
WHERE job = 'MANAGER'
UNION
SELECT empno, ename, sal, job, deptno, '高工资' AS 标签
FROM emp
WHERE sal > 3000;
输出:
bash
+-------+-------+--------+-----------+--------+------+
| empno | ename | sal | job | deptno | 标签 |
+-------+-------+--------+-----------+--------+------+
| 7566 | JONES | 2975.00| MANAGER | 20 | MANAGER|
| 7698 | BLAKE | 2850.00| MANAGER | 30 | MANAGER|
| 7782 | CLARK | 2450.00| MANAGER | 10 | MANAGER|
| 7788 | SCOTT | 3000.00| ANALYST | 20 | 高工资|
| 7839 | KING | 5000.00| PRESIDENT | 10 | 高工资|
| 7902 | FORD | 3000.00| ANALYST | 20 | 高工资|
+-------+-------+--------+-----------+--------+------+
九、总结与下一步
现在你已经掌握了:
✅ 多表查询:笛卡尔积、等值连接、三表连接
✅ 自连接:同一表的自我关联查询
✅ 子查询:单行、多行、多列、FROM 子句中的子查询
✅ UNION:合并多个 SELECT 的结果,去重和不去重
✅ 复杂查询案例:多层嵌套、综合应用
10.1 复合查询的核心要点
- 明确需求:需要从哪些表获取数据?
- 确定关系:表之间的外键关系是什么?
- 选择方法:多表连接、子查询、还是 UNION?
- 优化性能:索引的使用、避免笛卡尔积等。
- 测试验证:确保结果的正确性。
10.2 何时使用哪种方法
| 场景 | 推荐方法 |
|---|---|
| 两个表简单关联 | INNER JOIN |
| 需要保留不匹配数据 | LEFT/RIGHT JOIN |
| 查询某个值的员工 | 子查询 + WHERE |
| 需要临时计算(如平均值) | FROM 子句中的子查询 |
| 来自不同表的不同条件 | UNION |
| 层级关系 | 自连接 |
10.3 建议练习
- 使用多表连接和子查询实现相同的查询,比较结果。
- 尝试用多种方法解决同一个问题。
- 在你自己的项目中应用这些技术。
- 分析查询的执行计划(使用 EXPLAIN)。
下一篇,我们将学习内外连接:详细讲解各种连接类型。