SQL 多表查询核心知识点复述
在 SQL 中,多表查询指从两个或多个表中检索数据并组合,核心实现方式是 JOIN 操作 ,也可通过 子查询 结合多表数据。其核心价值是整合分散在不同表中的关联信息(如员工表与部门表通过 "部门 ID" 关联),以下是具体知识点:
一、JOIN 操作(多表查询核心)
JOIN 是多表查询最常用方式,通过定义表间关联条件(通常是外键与主键匹配)合并数据,返回统一结果集。根据 "匹配规则" 不同,分为以下 5 种类型:
1. INNER JOIN(内连接)
-
规则:仅返回 "两个表中都有匹配记录" 的行;若某表无对应匹配,该行不纳入结果。
-
场景:需获取 "双方都存在关联数据" 的结果(如查询 "有对应部门的员工")。
-
示例 :
sqlSELECT e.name, d.department_name FROM employees e -- 别名 e 简化表名 INNER JOIN departments d ON e.department_id = d.department_id; -- 关联条件:员工部门ID=部门ID
结果仅包含 "在 employees 表有记录、且在 departments 表有对应部门" 的员工及其部门名。
2. LEFT JOIN(左连接,LEFT OUTER JOIN 全称)
-
规则:返回 "左边表(第一个表)的所有行";若右边表(第二个表)无匹配记录,右边表的列显示为 NULL。
-
场景:需 "完整保留左表数据",同时关联右表信息(如查询 "所有员工,包括无部门的员工")。
-
示例 :
sql
sqlSELECT e.name, d.department_name FROM employees e LEFT JOIN departments d ON e.department_id = d.department_id;
结果包含所有员工(即使无对应部门),无部门的员工其
department_name
列显示 NULL。
3. RIGHT JOIN(右连接,RIGHT OUTER JOIN 全称)
-
规则:返回 "右边表(第二个表)的所有行";若左边表无匹配记录,左边表的列显示为 NULL。
-
场景:需 "完整保留右表数据",同时关联左表信息(如查询 "所有部门,包括无员工的部门")。
-
示例 :
sql
sqlSELECT e.name, d.department_name FROM employees e RIGHT JOIN departments d ON e.department_id = d.department_id;
结果包含所有部门(即使无员工),无员工的部门其
name
列显示 NULL。
4. FULL JOIN(全连接,FULL OUTER JOIN 全称)
-
规则:返回 "两个表的所有记录";若某表无匹配行,另一表的对应列显示为 NULL。
-
场景:需 "完整保留两个表的所有数据",直观展示双方匹配 / 不匹配情况(如查询 "所有员工 + 所有部门,显示匹配关系")。
-
示例 :
sql
sqlSELECT e.name, d.department_name FROM employees e FULL JOIN departments d ON e.department_id = d.department_id;
结果包含所有员工和所有部门,无匹配的行对应列显示 NULL。
5. CROSS JOIN(交叉连接)
-
规则 :返回 "两个表的笛卡尔积"(左表每一行与右表每一行强制组合),无需 ON 关联条件。
-
场景:需 "枚举所有组合可能性"(极少用,需注意结果集规模,如左表 10 行、右表 5 行,结果为 50 行)。
-
示例 :
sql
sqlSELECT e.name, p.product_name FROM employees e CROSS JOIN products p;
结果包含 "每个员工与每个产品" 的所有组合(无实际业务关联,仅枚举)。
二、自连接(Self JOIN)
-
定义:将 "单个表与自身进行 JOIN",本质是把一张表 "虚拟拆分为两张表",通过表内关联字段(如 "经理 ID" 与 "员工 ID")查询内部关联数据。
-
场景:需查询表内 "层级 / 关联关系"(如员工与上级经理、学生与同班同学)。
-
示例 :
sql
sqlSELECT e1.name AS employee_name, e2.name AS manager_name FROM employees e1 -- 虚拟为"员工表" LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id; -- 关联条件:员工的经理ID=另一行的员工ID
结果返回 "每个员工及其对应的经理名",无经理的员工其
manager_name
显示 NULL。
三、多表查询与聚合操作结合
-
规则 :聚合函数(COUNT、SUM、AVG、MAX、MIN)可与 JOIN 配合,对多表关联后的数据进行统计汇总,需结合
GROUP BY
分组。 -
场景:需 "按维度统计多表关联数据"(如统计各部门员工数、各项目总预算)。
-
示例 :
sql
sqlSELECT d.department_name, COUNT(e.employee_id) AS num_employees FROM departments d LEFT JOIN employees e ON e.department_id = d.department_id -- 关联部门与员工 GROUP BY d.department_name; -- 按部门分组统计
结果返回 "每个部门名及该部门员工数",无员工的部门其
num_employees
显示 0(因 LEFT JOIN 保留了所有部门)。
四、多表查询中的条件筛选(WHERE 与 HAVING)
多表查询中,筛选分为 "关联后筛选行" 和 "聚合后筛选分组",分别对应 WHERE
和 HAVING
:
1. WHERE 子句
-
作用 :对 JOIN 关联后的 "行数据" 进行筛选,在聚合函数执行前生效(不能筛选聚合结果)。
-
场景:需 "过滤不符合条件的原始数据"(如查询薪资 > 5000 的员工及其部门)。
-
示例 :
sql
sqlSELECT e.name, d.department_name FROM employees e INNER JOIN departments d ON e.department_id = d.department_id WHERE e.salary > 5000; -- 筛选薪资>5000的员工
2. HAVING 子句
-
作用 :对
GROUP BY
聚合后的 "分组数据" 进行筛选,在聚合函数执行后生效(仅能筛选聚合结果)。 -
场景:需 "过滤不符合条件的聚合分组"(如查询员工数 > 5 的部门)。
-
示例 :
sql
sqlSELECT d.department_name, COUNT(e.employee_id) AS num_employees FROM departments d LEFT JOIN employees e ON e.department_id = d.department_id GROUP BY d.department_name HAVING COUNT(e.employee_id) > 5; -- 筛选"员工数>5"的部门
五、复杂多表查询(连接 3 个及以上表)
-
规则:通过 "连续 JOIN" 关联多个表,每个 JOIN 都需定义明确的关联条件(通常是前表的外键与后表的主键),关联顺序不影响结果(需确保逻辑连贯)。
-
场景:需整合 "多层级关联数据"(如员工→部门→公司、学生→班级→学校)。
-
示例 :查询员工、所属部门及参与的项目:
sql
sqlSELECT e.name, d.department_name, p.project_name FROM employees e INNER JOIN departments d ON e.department_id = d.department_id -- 员工→部门 INNER JOIN projects p ON e.employee_id = p.employee_id; -- 员工→项目
结果返回 "每个员工的姓名、所属部门、参与的项目"(仅包含三方都有匹配的记录)。
六、避免重复记录(DISTINCT 与 GROUP BY)
多表查询可能因 "一对多" 关系(如一个员工参与多个项目)产生重复数据,需通过以下方式去重或分组:
1. DISTINCT 关键字
-
作用:去除结果集中 "完全重复的行",保留唯一记录(仅针对查询列的组合去重)。
-
场景:需 "获取唯一值"(如查询员工涉及的所有部门 ID,避免重复)。
-
示例 :
sql
sqlSELECT DISTINCT e.department_id FROM employees e INNER JOIN departments d ON e.department_id = d.department_id;
结果返回 "员工涉及的所有唯一部门 ID",即使同一部门有多个员工,也只显示一次。
2. GROUP BY 子句
-
作用:按指定字段分组,结合聚合函数统计,间接避免重复(按分组维度聚合,而非单纯去重)。
-
场景:需 "按维度统计"(如统计每个部门的员工数,天然避免重复计数)。
-
示例 :
sql
sqlSELECT e.department_id, COUNT(e.employee_id) FROM employees e GROUP BY e.department_id;
结果按 "部门 ID" 分组,统计每个部门的员工数,无重复部门 ID。
总结
多表查询是 SQL 中整合关联数据的核心技术,核心逻辑是通过 JOIN 操作 定义表间关联规则,通过 聚合函数 + GROUP BY 实现统计,通过 WHERE/HAVING 实现筛选。实际使用中需根据业务需求选择合适的 JOIN 类型(如保留左表用 LEFT JOIN、双方匹配用 INNER JOIN),并注意避免重复数据,确保结果准确且高效。
在 SQL 多表查询中,除了显式使用 INNER JOIN
关键字的 "显式内连接",还有一种不写 JOIN
关键字、通过 FROM
子句多表并列 +WHERE
子句指定关联条件的 "隐式内连接"。它本质是内连接的简化写法,核心逻辑与显式内连接完全一致 ------仅返回两个表中匹配关联条件的行,但语法形式不同。
一、隐式内连接的语法规则
核心结构
sql
sql
SELECT 列名1, 列名2, ...
FROM 表1, 表2 -- 多个表在FROM后用逗号分隔(表的顺序不影响结果)
WHERE 表1.关联字段 = 表2.关联字段; -- 必须通过WHERE指定"表间匹配条件",否则会产生笛卡尔积
- 关键:用
FROM 表1, 表2
代替INNER JOIN
关键字 ,用WHERE
子句代替ON
子句来定义关联条件。 - 注意:若省略
WHERE
关联条件,会直接返回两个表的笛卡尔积(与CROSS JOIN
效果一致),通常是错误结果,需格外注意。
二、与显式内连接的对比(用案例说明)
以 "查询员工姓名及所属部门名" 为例(基于 employees
员工表和 departments
部门表,关联字段为 department_id
):
1. 隐式内连接(不写 JOIN)
sql
sql
SELECT
e.name AS 员工姓名,
d.department_name AS 部门名
FROM
employees e, -- 表1,别名e
departments d -- 表2,别名d(用逗号分隔)
WHERE
e.department_id = d.department_id; -- WHERE指定关联条件:员工部门ID=部门ID
2. 显式内连接(写 INNER JOIN + ON)
sql
sql
SELECT
e.name AS 员工姓名,
d.department_name AS 部门名
FROM
employees e
INNER JOIN
departments d
ON
e.department_id = d.department_id; -- ON指定关联条件
结果完全一致
两种写法返回的结果完全相同 :仅包含 "在员工表和部门表中都有匹配 department_id
" 的记录,无匹配的行(如无部门的员工、无员工的部门)会被排除。
三、隐式内连接的扩展场景
1. 多表隐式内连接(3 个及以上表)
若需关联 3 个表(如 employees
员工表、departments
部门表、projects
项目表),只需在 FROM
后继续加表,在 WHERE
后补充所有表的关联条件即可。
示例:查询员工姓名、部门名、参与的项目名
sql
sql
SELECT
e.name AS 员工姓名,
d.department_name AS 部门名,
p.project_name AS 项目名
FROM
employees e,
departments d,
projects p -- FROM后3个表用逗号分隔
WHERE
e.department_id = d.department_id -- 员工→部门的关联条件
AND e.employee_id = p.employee_id; -- 员工→项目的关联条件(用AND连接多个条件)
2. 结合 WHERE 筛选非关联条件
隐式内连接中,WHERE
子句可同时包含 "表间关联条件" 和 "数据筛选条件"(用 AND
连接),逻辑与显式内连接一致。
示例:查询 "薪资> 8000 的员工姓名及所属部门名"
sql
sql
SELECT
e.name AS 员工姓名,
d.department_name AS 部门名
FROM
employees e,
departments d
WHERE
e.department_id = d.department_id -- 关联条件(必须有)
AND e.salary > 8000; -- 筛选条件(薪资>8000)
四、隐式内连接的优缺点
优点
- 语法更简洁:少写
INNER JOIN
和ON
关键字,适合简单的内连接场景。 - 兼容性好:早期 SQL 标准中没有显式
JOIN
语法,隐式内连接在老系统或简化查询中仍常用。
缺点
- 可读性差:当表数量较多(如 3 个以上)时,
FROM
后逗号分隔的表、WHERE
中混合的 "关联条件" 和 "筛选条件" 容易混淆,不如显式JOIN
(ON
管关联、WHERE
管筛选)逻辑清晰。 - 易出错:若不小心遗漏
WHERE
关联条件,会直接产生笛卡尔积(数据量暴增,结果无意义),而显式JOIN
若漏写ON
会直接报语法错误,更容易排查。
五、使用建议
- 简单场景(2 个表的内连接):可使用隐式内连接,语法简洁。
- 复杂场景(3 个及以上表、需区分关联 / 筛选逻辑):优先用显式内连接 (
INNER JOIN ... ON ...
),可读性和可维护性更强,降低出错概率。 - 无论哪种写法,核心逻辑一致:都是 "通过关联条件匹配多表数据,仅保留匹配行"。