学习笔记:MYSQL(4)

SQL 多表查询核心知识点复述

在 SQL 中,多表查询指从两个或多个表中检索数据并组合,核心实现方式是 JOIN 操作 ,也可通过 子查询 结合多表数据。其核心价值是整合分散在不同表中的关联信息(如员工表与部门表通过 "部门 ID" 关联),以下是具体知识点:

一、JOIN 操作(多表查询核心)

JOIN 是多表查询最常用方式,通过定义表间关联条件(通常是外键与主键匹配)合并数据,返回统一结果集。根据 "匹配规则" 不同,分为以下 5 种类型:

1. INNER JOIN(内连接)

  • 规则:仅返回 "两个表中都有匹配记录" 的行;若某表无对应匹配,该行不纳入结果。

  • 场景:需获取 "双方都存在关联数据" 的结果(如查询 "有对应部门的员工")。

  • 示例

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT e.name, p.product_name
    FROM employees e
    CROSS JOIN products p;

    结果包含 "每个员工与每个产品" 的所有组合(无实际业务关联,仅枚举)。

二、自连接(Self JOIN)

  • 定义:将 "单个表与自身进行 JOIN",本质是把一张表 "虚拟拆分为两张表",通过表内关联字段(如 "经理 ID" 与 "员工 ID")查询内部关联数据。

  • 场景:需查询表内 "层级 / 关联关系"(如员工与上级经理、学生与同班同学)。

  • 示例

    sql

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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)

多表查询中,筛选分为 "关联后筛选行" 和 "聚合后筛选分组",分别对应 WHEREHAVING

1. WHERE 子句

  • 作用 :对 JOIN 关联后的 "行数据" 进行筛选,在聚合函数执行前生效(不能筛选聚合结果)。

  • 场景:需 "过滤不符合条件的原始数据"(如查询薪资 > 5000 的员工及其部门)。

  • 示例

    sql

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT 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

    sql 复制代码
    SELECT DISTINCT e.department_id
    FROM employees e
    INNER JOIN departments d ON e.department_id = d.department_id;

    结果返回 "员工涉及的所有唯一部门 ID",即使同一部门有多个员工,也只显示一次。

2. GROUP BY 子句

  • 作用:按指定字段分组,结合聚合函数统计,间接避免重复(按分组维度聚合,而非单纯去重)。

  • 场景:需 "按维度统计"(如统计每个部门的员工数,天然避免重复计数)。

  • 示例

    sql

    sql 复制代码
    SELECT 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 JOINON 关键字,适合简单的内连接场景。
  • 兼容性好:早期 SQL 标准中没有显式 JOIN 语法,隐式内连接在老系统或简化查询中仍常用。
缺点
  • 可读性差:当表数量较多(如 3 个以上)时,FROM 后逗号分隔的表、WHERE 中混合的 "关联条件" 和 "筛选条件" 容易混淆,不如显式 JOINON 管关联、WHERE 管筛选)逻辑清晰。
  • 易出错:若不小心遗漏 WHERE 关联条件,会直接产生笛卡尔积(数据量暴增,结果无意义),而显式 JOIN 若漏写 ON 会直接报语法错误,更容易排查。

五、使用建议

  • 简单场景(2 个表的内连接):可使用隐式内连接,语法简洁。
  • 复杂场景(3 个及以上表、需区分关联 / 筛选逻辑):优先用显式内连接INNER JOIN ... ON ...),可读性和可维护性更强,降低出错概率。
  • 无论哪种写法,核心逻辑一致:都是 "通过关联条件匹配多表数据,仅保留匹配行"。
相关推荐
MarkHD3 小时前
智能体在车联网中的应用:第51天 模仿学习与离线强化学习:破解数据效率与安全困局的双刃剑
学习·安全
tokepson4 小时前
Mysql下载部署方法备份(Windows/Linux)
linux·服务器·windows·mysql
Drawing stars5 小时前
JAVA后端 前端 大模型应用 学习路线
java·前端·学习
崇山峻岭之间6 小时前
Matlab学习记录33
开发语言·学习·matlab
玄〤6 小时前
黑马点评中 VoucherOrderServiceImpl 实现类中的一人一单实现解析(单机部署)
java·数据库·redis·笔记·后端·mybatis·springboot
科技林总6 小时前
【系统分析师】3.5 多处理机系统
学习
芯思路8 小时前
STM32开发学习笔记之三【按键】
笔记·stm32·学习
什么都不会的Tristan8 小时前
MybatisPlus-扩展功能
数据库·mysql
Lips6118 小时前
2026.1.11力扣刷题笔记
笔记·算法·leetcode
charlie1145141918 小时前
从 0 开始的机器学习——NumPy 线性代数部分
开发语言·人工智能·学习·线性代数·算法·机器学习·numpy