MySQL

MySQL 基础知识面试题

·32 分钟阅读·12654 字
📋 目录

MySQL 基础知识面试题


1. 关系型与非关系型数据库的区别

【题目】

关系型数据库与非关系型数据库有什么区别?

【参考答案】

关系型数据库(RDBMS)基于表格结构,遵循 ACID 原则,支持 SQL 标准,提供事务处理能力,具有固定的模式和数据一致性保证。代表产品:MySQL、Oracle、PostgreSQL。

非关系型数据库(NoSQL)更加灵活,适应键值对、文档、列族、图形等不同数据模型,牺牲部分 ACID 特性以换取水平扩展能力高性能。代表产品:Redis、MongoDB、Cassandra。

维度关系型数据库非关系型数据库
数据模型二维表格Key-Value、文档、列族、图
事务支持支持 ACID通常支持 BASE(最终一致性)
扩展方式垂直扩展为主水平扩展为主
适用场景金融、ERP 等强一致性场景高并发、海量数据、灵活模式

【延伸追问】

  1. 什么场景下会考虑从关系型数据库迁移到 NoSQL?
  2. 分布式事务在 NoSQL 中如何实现最终一致性?
  3. CAP 理论在数据库选型中如何应用?

【易错坑点】

  • ❌ 误以为 NoSQL 完全不支持事务(实际上像 MongoDB 支持多文档事务)
  • ❌ 混淆 BASE 和 ACID 的适用边界

2. 什么是 SQL?SQL 有哪些功能分类?

【题目】

什么是 SQL?SQL 语句分为哪些子类?

【参考答案】

SQL(Structured Query Language)—— 结构化查询语言,是访问关系型数据库的通用语言。

分类全称常见语句作用
DQLData Query LanguageSELECT数据查询
DMLData Manipulation LanguageINSERT、UPDATE、DELETE、MERGE数据增删改
DDLData Definition LanguageCREATE、ALTER、DROP定义数据库对象(表、索引等)
TCLTransaction Control LanguageCOMMIT、ROLLBACK、SAVEPOINT事务管理
DCLData Control LanguageGRANT、REVOKE权限控制

【延伸追问】

  1. TRUNCATE 属于哪一类?为什么有些场景下它比 DELETE 更快?
  2. MERGE 语句(UPSERT)的实现原理是什么?

【易错坑点】

  • ❌ 容易漏掉 TCL(事务控制语言)分类
  • ❌ 误认为 TRUNCATE 是 DML(实际是 DDL,因为隐式 COMMIT)

3. CHAR 和 VARCHAR 的区别

【题目】

CHAR 和 VARCHAR 类型有什么区别?

【参考答案】

特性CHARVARCHAR
长度固定长度可变长度
存储用空格填充到定义长度按实际长度 + 1~2 字节前缀
性能略快(固定长度无需计算)略慢(需计算实际长度)
适用固定长度编码、MD5、UUID姓名、地址等长度不一的字段

核心:CHAR(N) 中 N 是字符数,不是字节数。UTF-8 下每个字符可能占 1~4 字节。

【延伸追问】

  1. VARCHAR(255) 和 VARCHAR(256) 在存储上有什么区别?(提示:长度前缀字节数不同)
  2. 为什么建议用 CHAR 存 MD5(32 位定长)?

【易错坑点】

  • ❌ 混淆字符数和字节数:CHAR(10) 在 UTF-8 下最多占 40 字节
  • ❌ 误以为 VARCHAR 一定节省空间(VARCHAR 有额外长度前缀开销)

4. 什么是自增列?

【题目】

什么是自增列(Auto Increment)?它的用途是什么?

【参考答案】

自增列(AUTO_INCREMENT)用于生成自动增长的数字,主要用途是为主键提供唯一值

sql CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) );

特性:

  • 默认从 1 开始,步长为 1
  • 在 InnoDB 中,自增值保存在内存中,重启后通过 SELECT MAX(id) 恢复
  • 可自定义起始值和步长:AUTO_INCREMENT = 1000

【延伸追问】

  1. MySQL 重启后自增列的值会怎样变化?(InnoDB 在 8.0 之前有 bug,重启后可能跳号)
  2. 自增锁(AUTO-INC Locking)在什么情况下会成为性能瓶颈?

【易错坑点】

  • ❌ 误以为自增列一定连续(事务回滚会导致跳号)
  • ❌ 认为自增列只能在主键上使用(实际上只要是唯一索引即可)

5. DROP TABLE 和 TRUNCATE TABLE 的区别

【题目】

DROP TABLE 和 TRUNCATE TABLE 有什么区别?

【参考答案】

特性DROP TABLETRUNCATE TABLE
删除内容表结构 + 数据 + 索引 + 约束 + 授权仅删除数据
保留表结构❌ 不保留✅ 保留
速度快(直接删除 .ibd 文件)快(释放数据页)
可回滚❌ 不可回滚(DDL 隐式 COMMIT)❌ 不可回滚
触发器触发不触发不触发

TRUNCATE 本质上是先 DROP 表再 CREATE 表,所以比 DELETE 快。

【延伸追问】

  1. DROP TABLE 后如何恢复数据?(依赖最近的物理备份 + Binlog)
  2. TRUNCATE 和 DELETE FROM table 不带 WHERE 有何本质区别?

【易错坑点】

  • ❌ 误以为 TRUNCATE 可以回滚(InnoDB 下 TRUNCATE 是 DDL,隐式提交)
  • ❌ 混淆 TRUNCATE 和 DELETE FROM table_name

6. COUNT(*) 与 COUNT(列名) 的区别

【题目】

COUNT(*) 与 COUNT(列名) 的区别是什么?

【参考答案】

函数行为NULL 处理
COUNT(*)统计表中所有行数包含 NULL 行
COUNT(列名)统计该列非 NULL 值的行数忽略 NULL

sql -- 假设表有 5 行,某列有 2 个 NULL SELECT COUNT(*) FROM t; -- 返回 5 SELECT COUNT(col) FROM t; -- 返回 3(忽略 2 个 NULL)

性能:MySQL 8.0 优化后,COUNT(*) 和 COUNT(1) 性能相当。

【延伸追问】

  1. COUNT(1) 和 COUNT(*) 哪个更快?(MySQL 8.0 中无区别)
  2. MyISAM 下 COUNT(*) 为何比 InnoDB 快?(MyISAM 缓存了表总行数)

【易错坑点】

  • ❌ 误以为 COUNT(*) 会统计所有列,性能更差(实际 MySQL 已优化)
  • ❌ 混淆 NULL 和空字符串 ”(空字符串不是 NULL)

7. MySQL 日志文件有哪些?分别介绍作用

【题目】

MySQL 有哪些日志文件?分别介绍它们的作用。

【参考答案】

日志类型引擎作用
错误日志(Error Log)所有记录启动、运行、关闭时的错误信息,排查问题时优先查看
慢查询日志(Slow Query Log)所有记录执行时间超过 long_query_time 的 SQL,用于定位慢查询
一般查询日志(General Log)所有记录所有对数据库的请求(无论正确与否),性能开销大,平时关闭
二进制日志(Binlog)所有记录所有 DDL/DML 操作(不包含 SELECT),用于主从复制和恢复
重做日志(Redo Log)InnoDB记录事务修改,保证持久性(崩溃恢复用)
回滚日志(Undo Log)InnoDB记录数据修改前的状态,保证原子性和 MVCC

【延伸追问】

  1. Redo Log 和 Binlog 有什么区别?(物理日志 vs 逻辑日志,崩溃恢复 vs 时间点恢复)
  2. 两阶段提交(2PC)如何保证 Redo Log 和 Binlog 的一致性?

【易错坑点】

  • ❌ 混淆 Binlog(Server 层)和 Redo Log(InnoDB 引擎层)
  • ❌ 忽略 Undo Log 在 MVCC 中的作用

8. 主从复制原理

【题目】

MySQL 主从复制的原理是什么?

【参考答案】

主从复制流程分为三步

Master: 写入 → Binlog ↓ (Dump Thread 推送) Slave: IO Thread → Relay Log ↓ (SQL Thread 重放) Slave: 数据更新

  1. Master 写入 Binlog:Master 上的数据变更记录到 Binary Log
  2. Dump Thread 推送:Master 创建 Dump 线程,向 Slave 推送 Binlog
  3. IO Thread 接收:Slave 连接 Master,创建 IO 线程接收 Binlog 并写入 Relay Log
  4. SQL Thread 重放:Slave 的 SQL 线程读取 Relay Log 并在本地执行
  5. Slave 记录自己的 Binlog(如果需要级联复制)

【延伸追问】

  1. 主从延迟的原因有哪些?如何排查?
  2. 半同步复制与异步复制的区别是什么?
  3. GTID 复制相比传统复制的优势是什么?

【易错坑点】

  • ❌ 误以为 Slave 直接应用 Master 的 Binlog(实际通过 Relay Log 中转)
  • ❌ 忽视 Dump Thread 是 Master 端的线程

9. InnoDB 和 MyISAM 引擎的区别

【题目】

MySQL 中 InnoDB 和 MyISAM 存储引擎的区别是什么?

【参考答案】

特性InnoDBMyISAM
事务✅ 支持(ACID)❌ 不支持
锁粒度行级锁(高并发)表级锁(低并发)
外键✅ 支持❌ 不支持
全文索引MySQL 5.6+ 支持✅ 原生支持
缓存缓存索引 + 数据(Buffer Pool)只缓存索引(Key Cache)
行数统计实时 COUNT 需扫描缓存总行数,COUNT 极快
存储文件.ibd(数据+索引).MYD(数据)+ .MYI(索引)
默认引擎MySQL 5.5+ 默认MySQL 5.5 之前默认

【延伸追问】

  1. 为什么 MyISAM 的 COUNT 比 InnoDB 快得多?
  2. 在哪些业务场景下仍会使用 MyISAM?(只读数据仓库场景)

【易错坑点】

  • ❌ 误以为 InnoDB 一定比 MyISAM 好(MyISAM 在只读场景下索引查询更快)
  • ❌ 忽略 InnoDB 的 MVCC 机制对并发控制的重要性

10. InnoDB 为什么推荐使用自增 ID 作为主键

【题目】

为什么 InnoDB 推荐使用自增 ID 作为主键?

【参考答案】

InnoDB 使用聚簇索引(Clustered Index),主键索引的叶子节点直接存储行数据。

  • 自增 ID 有序插入:新数据追加到 B+ 树末尾,减少页分裂和碎片
  • UUID/随机主键:插入位置随机,导致频繁的页分裂、数据迁移和性能下降
  • 空间利用:自增 ID(INT 4 字节 / BIGINT 8 字节)比 UUID(36 字节)占用更少的索引空间

`sql — 推荐 CREATE TABLE t (id INT AUTO_INCREMENT PRIMARY KEY, …);

— 不推荐(UUID 主键会导致频繁页分裂) CREATE TABLE t (id CHAR(36) PRIMARY KEY, …); `

【延伸追问】

  1. 分布式场景下自增 ID 有什么局限性?如何解决?(雪花算法、号段模式)
  2. 如果使用 UUID 做主键,如何优化 InnoDB 的插入性能?

【易错坑点】

  • ❌ 误以为自增 ID 一定连续(事务回滚导致跳号)
  • ❌ 忽略 UUID 主键导致索引体积膨胀和页分裂问题

11. 事务的 ACID 特性

【题目】

解释事务的 ACID 四个特性。

【参考答案】

特性说明实现机制
原子性(Atomicity)事务中的操作不可分割,要么全部成功,要么全部回滚Undo Log 记录回滚信息
一致性(Consistency)事务执行前后数据库都处于一致状态由原子性 + 隔离性 + 业务逻辑共同保证
隔离性(Isolation)并发事务之间互不干扰锁 + MVCC(多版本并发控制)
持久性(Durability)事务提交后修改永久生效Redo Log + 双写缓冲(Double Write Buffer)

【延伸追问】

  1. MVCC 如何实现 RC 和 RR 隔离级别?
  2. 持久性是否意味着数据一定不会丢?(Group Commit、Binlog 刷盘策略影响)

【易错坑点】

  • ❌ 误以为一致性是数据库单独保证的(业务逻辑也要保证一致性)
  • ❌ 混淆持久性和「立即落盘」(实际有 Buffer Pool 缓冲)

12. 聚集索引与非聚集索引的区别

【题目】

聚集索引(Clustered Index)和非聚集索引(Secondary Index)的区别是什么?

【参考答案】

特性聚集索引非聚集索引
叶子节点直接包含完整行数据包含主键值(指向数据的指针)
物理顺序数据按索引顺序物理存储索引顺序与数据物理顺序无关
回表查询无需回表需要根据主键值回表查询
数量每张表只能有一个每张表可以有多个

InnoDB = 聚簇索引(主键),主键索引即数据。

回表过程:辅助索引 → 找到主键值 → 聚簇索引 → 找到完整行数据。

【延伸追问】

  1. 什么是覆盖索引?如何避免回表?
  2. 什么是索引下推(Index Condition Pushdown, ICP)?

【易错坑点】

  • ❌ 误以为 MyISAM 也有聚簇索引(MyISAM 的索引都是非聚簇的)
  • ❌ 忽略「回表」对查询性能的影响

13. 针对高并发插入优化 MySQL

【题目】

如何针对高并发插入场景优化 MySQL?

【参考答案】

  1. 使用自增主键:避免随机 I/O 和页分裂
  2. 调整 Buffer Pool:innodb_buffer_pool_size 设为物理内存的 60%~80%
  3. 分区表策略:按时间或业务维度分区,分散写入压力
  4. 批量插入:使用 INSERT INTO t VALUES (…), (…), (…) 代替逐条插入
  5. 调整刷盘策略:innodb_flush_log_at_trx_commit=2(允许每秒刷盘,提升写入性能)
  6. 减少索引:高频插入的表避免过多索引
  7. 使用写入缓冲:先写入队列/缓存,批量刷入数据库

【延伸追问】

  1. innodb_flush_log_at_trx_commit=1 和 =2 的区别是什么?
  2. 插入缓冲(Insert Buffer / Change Buffer)的原理是什么?

【易错坑点】

  • ❌ 为提升插入性能盲目调大 Buffer Pool(过大导致内存争抢)
  • ❌ 忽略 binlog 刷盘策略对写入性能的影响

14. 分库分表设计

【题目】

什么是分库分表?如何设计?

【参考答案】

垂直切分:按业务模块拆分表到不同库(如用户库、订单库) 水平切分:按数据范围/哈希将同一张表的数据分到多个库/表

常用中间件:ShardingSphereMyCatVitess

sql -- 水平分片示例(按用户 ID 取模分 4 张表) -- user_0, user_1, user_2, user_3 -- 路由规则:user_id % 4 = 表后缀

设计要点:

  • 分片键选择(按查询频率最高的字段分片)
  • 跨分片查询(全局表、ER 分片、广播表)
  • 分布式事务(Seata、XA、TCC)

【延伸追问】

  1. 分库分表后如何实现全局唯一 ID?(雪花算法、号段、Redis incr)
  2. 如何平滑扩容?(双写迁移、中间件路由切换)

【易错坑点】

  • ❌ 过早分库分表(大多数场景单库分区即可)
  • ❌ 忽略跨分片 JOIN 和分布式事务的复杂性

15. 主键索引和唯一索引的区别

【题目】

主键索引和唯一索引的区别是什么?

【参考答案】

特性主键索引唯一索引
NULL 值❌ 不允许✅ 允许(多个 NULL)
数量每张表只能有一个每张表可以有多个
索引类型聚簇索引(InnoDB)非聚簇索引(辅助索引)
物理存储决定数据物理存储顺序不决定物理顺序
用途唯一标识每一行保证列值的唯一性

【延伸追问】

  1. 没有主键的表,InnoDB 如何组织数据?(选择第一个唯一非空索引,或自动生成隐藏的 ROWID)
  2. 唯一索引对写入性能有什么影响?(每次写入需额外检查唯一性)

【易错坑点】

  • ❌ 误以为唯一索引不允许 NULL(实际上允许,且多个 NULL 不违反唯一约束)
  • ❌ 混淆主键约束和 NOT NULL + UNIQUE 索引的区别

16. MySQL 中的 JOIN 操作类型

【题目】

MySQL 中的 JOIN 操作有哪些类型?

【参考答案】

类型返回结果
INNER JOIN两表中都存在匹配的行
LEFT JOIN左表全部 + 右表匹配的行(无匹配显示 NULL)
RIGHT JOIN右表全部 + 左表匹配的行(无匹配显示 NULL)
FULL OUTER JOIN两表全部行(MySQL 不支持,用 LEFT + UNION + RIGHT 模拟)
CROSS JOIN笛卡尔积(两表所有行组合)

sql -- LEFT JOIN 示例 SELECT a.*, b.* FROM table_a a LEFT JOIN table_b b ON a.id = b.a_id;

【延伸追问】

  1. JOIN 的性能杀手有哪些?(嵌套循环、无索引关联列、临时表排序)
  2. 如何通过 EXPLAIN 分析 JOIN 的执行计划?

【易错坑点】

  • ❌ 混淆 LEFT JOIN 和 INNER JOIN 的结果行数
  • ❌ 忘记在 JOIN 的关联列上建索引

17. 什么是 MySQL 的索引?何时创建索引?

【题目】

什么是 MySQL 的索引?应该在什么情况下创建索引?

【参考答案】

索引是用于加速数据检索的数据结构(InnoDB 使用 B+ Tree)。

应创建索引的场景:

  • WHERE 子句中频繁出现的列
  • JOIN 关联列(关联条件上的列)
  • ORDER BY / GROUP BY 涉及的列
  • 覆盖索引可以避免回表查询
  • 高选择性列优先(区分度高的列)

不应创建索引的场景:

  • 数据量小的表(全表扫描更快)
  • 频繁更新的列(索引维护成本高)
  • 区分度低的列(如性别只有 2 个值)
  • 很少查询的列

【延伸追问】

  1. 如何通过 EXPLAIN 判断索引是否被正确使用?(type、key、Extra 字段)
  2. 什么是索引失效?常见的索引失效场景有哪些?

【易错坑点】

  • ❌ 索引越多越好(每多一个索引,写入就多一份开销)
  • ❌ 在区分度低的列上建索引(如性别列,B+ 树扫描效率不高)

18. 什么是 MySQL 事务?

【题目】

简述 MySQL 中的事务是什么?

【参考答案】

事务是数据库操作的基本单位,是一系列 SQL 操作的集合,这些操作要么全部执行成功,要么全部回滚

事务具有 ACID 特性(原子性、一致性、隔离性、持久性)。

sql START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; -- 或 ROLLBACK;

【延伸追问】

  1. 隐式事务和显式事务有什么区别?(autocommit 模式)
  2. 长事务有什么危害?(锁堆积、Undo 膨胀、主从延迟)

【易错坑点】

  • ❌ 误以为所有 SQL 都在事务中(MyISAM 不支持事务,DDL 自动隐式提交)
  • ❌ 忽略长事务导致 MVCC 版本的堆积问题

19. 什么是 MySQL 视图?

【题目】

什么是 MySQL 中的视图(View)?

【参考答案】

视图是从一个或多个表中派生出的虚拟表,不实际存储数据,只存储查询定义。

`sql — 创建视图 CREATE VIEW v_user_orders AS SELECT u.name, o.order_id, o.amount FROM users u JOIN orders o ON u.id = o.user_id;

— 使用视图(如同查询普通表) SELECT * FROM v_user_orders WHERE amount > 100; `

优点:

  • 简化复杂查询
  • 隐藏底层表结构,提供安全层
  • 逻辑隔离(表结构调整时,视图可保持不变)

限制:

  • 性能取决于底层查询(视图本质是封装了子查询)
  • 可更新视图有限制(聚合、DISTINCT、子查询等不可更新)

【延伸追问】

  1. 物化视图(Materialized View)和普通视图的区别是什么?
  2. 视图的更新(UPDATE VIEW)有什么限制条件?

【易错坑点】

  • ❌ 误以为视图能提升性能(视图只是封装的查询,不会缓存数据)
  • ❌ 忽略视图的性能开销(嵌套视图可能导致严重的性能问题)

20. 如何备份和恢复 MySQL 数据库?

【题目】

如何备份和恢复 MySQL 数据库?

【参考答案】

逻辑备份: `ash

mysqldump 备份

mysqldump -u root -p —all-databases > backup.sql mysqldump -u root -p db_name > db_backup.sql

恢复

mysql -u root -p < backup.sql mysql -u root -p db_name < db_backup.sql `

物理备份: `ash

Percona XtraBackup(热备,不锁表)

xtrabackup —backup —target-dir=/backup/ xtrabackup —prepare —target-dir=/backup/ `

备份方式优点缺点
mysqldump简单、可跨版本大数据量慢
XtraBackup快、热备、增量备份需额外安装工具
Binlog时间点恢复(PITR)需配合全量备份使用

【延伸追问】

  1. 如何进行时间点恢复(Point-In-Time Recovery)?
  2. 备份的恢复演练应该多久做一次?

【易错坑点】

  • ❌ 只有备份没有验证(备份文件损坏等于没备份)
  • ❌ 忽略 Binlog 的保留时间,导致无法 PITR

21. 索引不适合哪些场景?

【题目】

索引在哪些场景下不适合使用?

【参考答案】

  1. 数据量小的表:全表扫描比索引查询更快(MySQL 优化器会自动选择)
  2. 频繁更新的列:索引维护(B+ 树调整)开销大于查询收益
  3. 区分度低的列:如性别(男/女),扫描一半数据不如全表扫描
  4. 很少查询的列:索引占用空间,没有查询收益
  5. 长文本/二进制列:索引体积大且意义不大(可使用前缀索引)

【延伸追问】

  1. 什么是区分度(Cardinality)?如何查看?
  2. 前缀索引如何设置合适的长度?

【易错坑点】

  • ❌ 为所有列都建立索引(严重降低写入性能)
  • ❌ 忽略 MySQL 优化器可能不使用索引(即使有索引)

22. 脏读、不可重复读、幻读的解释

【题目】

什么是脏读、不可重复读、幻读?

【参考答案】

问题现象产生原因隔离级别解决
脏读事务 A 读到事务 B 未提交的数据写操作未提交即被读取READ COMMITTED 及以上
不可重复读同一事务内两次读取同一条记录返回不同数据其他事务修改并提交了该记录REPEATABLE READ 及以上
幻读同一事务内两次查询结果集数量不同(出现新行)其他事务插入/删除了记录SERIALIZABLE(或 InnoDB 的间隙锁解决)

sql -- 不可重复读示例 -- 事务 A: SELECT balance FROM accounts WHERE id = 1; -- 返回 100 -- 事务 B: UPDATE accounts SET balance = 200 WHERE id = 1; COMMIT; -- 事务 A: SELECT balance FROM accounts WHERE id = 1; -- 返回 200(不可重复读)

【延伸追问】

  1. InnoDB 如何通过 MVCC + Gap Lock 解决幻读?
  2. 在 READ COMMITTED 级别下如何避免不可重复读?

【易错坑点】

  • ❌ 混淆不可重复读(同一条记录值变化)和幻读(结果集行数变化)
  • ❌ 误以为 REPEATABLE READ 能完全避免幻读(InnoDB 的 Next-Key Lock 实际上解决的是「当前读」下的幻读)

23. SQL 约束有哪几种?

【题目】

SQL 约束有哪几种?

【参考答案】

约束说明示例
NOT NULL字段值不能为 NULLname VARCHAR(50) NOT NULL
UNIQUE字段值唯一,允许 NULLemail VARCHAR(100) UNIQUE
PRIMARY KEY唯一标识每行,不允许 NULLid INT PRIMARY KEY
FOREIGN KEY引用另一张表的主键,保证引用完整性FOREIGN KEY (dept_id) REFERENCES dept(id)
CHECK字段值必须满足指定条件(MySQL 8.0.16+ 才真正生效)age INT CHECK (age >= 0)
DEFAULT为字段指定默认值status INT DEFAULT 1

【延伸追问】

  1. CHECK 约束在 MySQL 8.0 之前为什么无效?(解析时忽略,不报错但也不检查)
  2. FOREIGN KEY 对性能有什么影响?(每次插入/更新需检查引用表)

【易错坑点】

  • ❌ 误以为 MySQL 的 CHECK 约束一直有效(MySQL 8.0.16 前仅解析不执行)
  • ❌ 忽略外键对写入性能的影响

24. UNION 和 UNION ALL 的区别

【题目】

UNION 和 UNION ALL 有什么区别?

【参考答案】

特性UNIONUNION ALL
重复行去重(自动 DEDUP)包含重复行
排序会对结果排序(以去重)不排序
性能较慢(需要额外排序去重)更快
内存需要临时表去重直接追加结果

`sql — UNION 去重效率低 SELECT name FROM employees_2023 UNION SELECT name FROM employees_2024;

— UNION ALL 效率高(确认无重复时使用) SELECT name FROM employees_2023 UNION ALL SELECT name FROM employees_2024; `

【延伸追问】

  1. UNION 的去重逻辑是什么?(对所有 SELECT 的列做 DISTINCT)
  2. 什么场景下应该用 UNION ALL 替代 UNION?

【易错坑点】

  • ❌ 随意使用 UNION 而不是 UNION ALL(导致不必要的去重排序开销)
  • ❌ 误以为 UNION 不会对结果排序(实际上为去重会排序)

25. 子查询及其用途

【题目】

解释子查询及其用途。

【参考答案】

子查询是嵌套在其他 SQL 查询中的查询,可以返回标量值、单列集合或多列结果。

`sql — 标量子查询(返回单个值) SELECT name, (SELECT MAX(salary) FROM employees) AS max_salary FROM departments;

— 行子查询(WHERE 条件中使用) SELECT * FROM products WHERE price > (SELECT AVG(price) FROM products);

— EXISTS 子查询 SELECT * FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id);

— FROM 子句中的派生表 SELECT avg_order.total FROM (SELECT customer_id, COUNT(*) as total FROM orders GROUP BY customer_id) avg_order; `

用途:

  • 在 WHERE 条件中比较聚合结果
  • 检查数据的存在性(EXISTS / NOT EXISTS)
  • 在 FROM 中作为派生表(临时结果集)

【延伸追问】

  1. 子查询和 JOIN 的性能差异是什么?(子查询在某些场景下会产生临时表,性能低于 JOIN)
  2. 关联子查询(Correlated Subquery)和非关联子查询的区别?

【易错坑点】

  • ❌ 滥用子查询导致性能问题(可用 JOIN 优化时优先使用 JOIN)
  • ❌ 忽略了 EXISTS 和 IN 子查询在某些场景下的性能差异

关联文档