Bohr-L Bohr-L
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

刘博

I'm a slow walker, But I never walk backwards.
首页
技术
常见面试题
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 数据处理与存储类

  • Spring 生态类

  • 缓存问题类

  • 多线程类

  • JVM 类

  • MySQL 类

    • MySQL 为什么一定要有一个主键
    • MySQL 中的 RR 隔离级别,到底有没有解决幻读问题
    • MySQL 的行级锁到底锁的是什么东西?
    • 存储 MD5 值应该用 VARCHAR 还是用 CHAR?
    • 数据库的三范式是什么?
    • 说说 InnoDB 与 MyISAM 的区别
    • drop、truncate、delete 的区别
    • 聊一聊数据库事务机制
    • 聊一聊 MySQL 中的关联查询
      • 事务隔离级别有哪些?MySQL 的默认隔离级别是什么
      • 分库分表之后,id 主键如何处理?
      • 说说在 MySQL 中一条查询 SQL 是如何执行的?
      • 讲解下 DDL、DML、DCL
      • 存储过程和触发器的作用
      • MySQL 如何行转列和列转行
      • 如何查看 SQL 的执行计划
      • union 和 unionAll 的区别
      • having 和 where 的区别
      • 常见的索引原则
      • MySQL 中的 IN 和 EXISTS 子句有什么区别?
      • MySQL 如何处理 NULL 值,对性能有什么影响?
      • 如何在 MySQL 中处理和避免全表扫描?
      • MySQL 中的表空间是什么,它的作用是什么?
      • 在 MySQL 中,如何优化 ORDER BY 查询?
    • Java 8 + 特性类

    • 其他技术类

    • 常见面试题
    • MySQL 类
    刘博
    2025-12-29
    目录

    聊一聊 MySQL 中的关联查询

    MySQL 的关联查询(Join Query)是 “从多个表中获取关联数据” 的核心方式,基于表间的关联条件(如外键)将多个表的数据组合返回,常见的关联类型包括内连接、外连接、交叉连接,具体如下:

    # 1. 关联查询的核心概念

    • 关联条件:用于匹配多个表的字段(如user.id = order.user_id),若缺少关联条件,会产生 “笛卡尔积”(两个表的所有行两两组合,数据量为两表行数乘积,通常是错误结果);
    • 驱动表与被驱动表:
      • 驱动表:关联查询时先执行的表(如SELECT * FROM user JOIN order ON user.id = order.user_id,user表是驱动表);
      • 被驱动表:关联查询时后执行的表(order表是被驱动表);
      • MySQL 会根据表的大小和索引选择驱动表(通常选择小表作为驱动表,减少被驱动表的查询次数)。

    # 2. 常见的关联查询类型

    以 “用户表(user,主键 id)” 和 “订单表(order,外键 user_id 关联 user.id)” 为例,说明各关联类型的差异:

    # (1)内连接(INNER JOIN):返回两表匹配的行
    • 定义:仅返回两个表中满足关联条件的行,不匹配的行(如无订单的用户、无用户的订单)不返回;

    • 语法:

      SELECT u.id, u.name, o.order_id
      FROM user u
      INNER JOIN `order` o ON u.id = o.user_id; -- 关联条件:user.id = order.user_id
      
      1
      2
      3
    • 结果示例:

      id name order_id
      1 张三 1001
      1 张三 1002
      2 李四 1003
    • 适用场景:查询同时存在于两个表中的数据(如 “有订单的用户及其订单”)。

    # (2)左外连接(LEFT JOIN):返回左表所有行,右表匹配的行
    • 定义:返回左表(JOIN 左侧的表)的所有行,右表(JOIN 右侧的表)中满足关联条件的行;若右表无匹配行,右表字段返回 NULL;

    • 语法:

      SELECT u.id, u.name, o.order_id
      FROM user u
      LEFT JOIN `order` o ON u.id = o.user_id;
      
      1
      2
      3
    • 结果示例:

      id name order_id
      1 张三 1001
      1 张三 1002
      2 李四 1003
      3 王五 NULL -- 王五无订单,order_id 返回 NULL
    • 适用场景:查询左表所有数据,无论右表是否有匹配(如 “所有用户及其订单,无订单的用户也显示”)。

    # (3)右外连接(RIGHT JOIN):返回右表所有行,左表匹配的行
    • 定义:与左外连接相反,返回右表的所有行,左表中满足关联条件的行;若左表无匹配行,左表字段返回 NULL;

    • 语法:

      SELECT u.id, u.name, o.order_id
      FROM user u
      RIGHT JOIN `order` o ON u.id = o.user_id;
      
      1
      2
      3
    • 结果示例:

      id name order_id
      1 张三 1001
      1 张三 1002
      2 李四 1003
      NULL NULL 1004 -- 订单 1004 无对应的用户,user 字段返回 NULL
    • 适用场景:查询右表所有数据,无论左表是否有匹配(如 “所有订单及其用户,无用户的订单也显示”)。

    # (4)全外连接(FULL JOIN):返回两表所有行(MySQL 不直接支持)
    • 定义:返回左表和右表的所有行,匹配的行合并,不匹配的行对应字段返回 NULL;

    • MySQL 实现方式:MySQL 不直接支持FULL JOIN,需通过LEFT JOIN + UNION + RIGHT JOIN实现:

      SELECT u.id, u.name, o.order_id
      FROM user u
      LEFT JOIN `order` o ON u.id = o.user_id
      UNION -- 合并结果,去重
      SELECT u.id, u.name, o.order_id
      FROM user u
      RIGHT JOIN `order` o ON u.id = o.user_id;
      
      1
      2
      3
      4
      5
      6
      7
    • 结果示例:包含左外连接和右外连接的所有行,无重复。

    # (5)交叉连接(CROSS JOIN):笛卡尔积
    • 定义:无关联条件的连接,返回两表所有行的笛卡尔积(数据量 = 左表行数 × 右表行数),通常无实际业务意义,需避免;

    • 语法:

      SELECT u.id, u.name, o.order_id
      FROM user u
      CROSS JOIN `order` o; -- 无关联条件
      
      1
      2
      3
    • 风险:若左表有 1000 行,右表有 1000 行,返回 100 万行数据,严重影响性能。

    # 3. 关联查询的性能优化

    关联查询的性能瓶颈通常在于 “被驱动表的查询效率”,核心优化思路是 “减少被驱动表的查询次数、利用索引加速匹配”,具体方案:

    • 1. 给关联字段建立索引:
      • 被驱动表的关联字段(如order.user_id)必须建立索引,避免被驱动表全表扫描(关联查询时,驱动表的每行数据都会去被驱动表匹配,无索引则每次匹配都是全表扫描);
      • 驱动表的关联字段若有索引,可提升驱动表的过滤效率(如user.id是主键索引,驱动表查询时可快速定位符合条件的行)。
    • 2. 选择合适的驱动表:
      • 遵循 “小表驱动大表” 原则:驱动表数据量越小,被驱动表的查询次数越少(如驱动表有 100 行,被驱动表查询 100 次;驱动表有 1000 行,被驱动表查询 1000 次);
      • MySQL 的优化器会自动选择小表作为驱动表,但需确保统计信息准确(可通过ANALYZE TABLE更新表统计信息)。
    • 3. 减少查询字段:
      • 避免SELECT *,只查询需要的字段(如SELECT u.name, o.order_id),减少数据传输和内存消耗;
      • 被驱动表的查询字段若包含在索引中(覆盖索引),可避免回表(如order表的索引idx_userid_orderid包含user_id和order_id,查询时无需访问数据文件)。
    • 4. 避免复杂条件在被驱动表:
      • 尽量在驱动表中过滤数据(如WHERE u.age > 20),减少驱动表的行数,从而减少被驱动表的查询次数;
      • 避免在被驱动表的关联字段上做函数操作(如ON u.id = SUBSTR(o.user_id_str, 2)),会导致索引失效。
    • 5. 控制关联表数量:
      • 避免超过 3 张表的关联查询(多表关联会增加优化器的计算复杂度,且容易导致索引失效);
      • 若需多表关联,可拆分查询(先查询两张表的结果,存入临时表,再与第三张表关联)。

    上次更新: 12/30/2025
    聊一聊数据库事务机制
    事务隔离级别有哪些?MySQL 的默认隔离级别是什么

    ← 聊一聊数据库事务机制 事务隔离级别有哪些?MySQL 的默认隔离级别是什么→

    最近更新
    01
    CPU 使用率较高排查和解决
    12-29
    02
    JVM OOM 问题如何排查和解决
    12-29
    03
    接口防刷怎么实现?
    12-29
    更多文章>
    Theme by Vdoing | Copyright © 2025-2026 Bohr-L's note
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式