学习笔记: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 ...),可读性和可维护性更强,降低出错概率。
  • 无论哪种写法,核心逻辑一致:都是 "通过关联条件匹配多表数据,仅保留匹配行"。
相关推荐
计算机学长felix6 小时前
基于Django的“酒店推荐系统”设计与开发(源码+数据库+文档+PPT)
数据库·python·mysql·django·vue
aaaweiaaaaaa6 小时前
c++基础学习(学习蓝桥杯 ros2有C基础可看)
c++·学习·蓝桥杯·lambda·ros2·智能指针·c++类
MadPrinter7 小时前
SpringBoot学习日记 Day11:博客系统核心功能深度开发
java·spring boot·后端·学习·spring·mybatis
wanzhong23338 小时前
ArcGIS学习-18 实战-降雨量空间分布插值分析
学习·arcgis
wanzhong23338 小时前
ArcGIS学习-19 实战-表面分析
学习
我命由我123458 小时前
Photoshop - Photoshop 创建文档
学习·ui·课程设计·设计·photoshop·ps·美工
@ZzHhXx8 小时前
嵌入式学习---(硬件)
学习
励志不掉头发的内向程序员9 小时前
C++进阶——多态
开发语言·c++·学习
007php0079 小时前
某大厂MySQL面试之SQL注入触点发现与SQLMap测试
数据库·python·sql·mysql·面试·职场和发展·golang