【MySQL全面教学】MySQL面试高频考点汇总Day15(2026年)


写在前面

大家好,欢迎来到MySQL全面教学系列的最后一篇------第15天。经过了前面14天的系统学习,我们已经从零开始,逐步掌握了MySQL的方方面面:

  • 基础篇:数据类型、SQL语句、字符集
  • 进阶篇:索引原理、事务机制、锁机制
  • 实战篇:视图触发器、性能优化、备份恢复

今天,我们将系统梳理所有面试高频考点,帮助你在面试中从容应对。这篇文章是汇总性质,内容较长,建议收藏后反复阅读。

让我们一起完成这最后的学习之旅!


目录

一、基础篇考点

1.1 数据类型选择

面试题:CHAR和VARCHAR的区别?

答案:

对比项 CHAR VARCHAR
存储方式 固定长度 变长
空间占用 可能浪费空间 节省空间
性能 略快 略慢(需要计算长度)
适用场景 固定长度(MD5、手机号) 变长数据(姓名、地址)
最大长度 255字符 65535字节
sql 复制代码
-- CHAR示例:手机号(固定11位)
phone CHAR(11)

-- VARCHAR示例:用户名(变长)
username VARCHAR(50)
面试题:DECIMAL和FLOAT/DOUBLE的区别?

答案:

  • DECIMAL:精确小数,定点数存储,适合金额计算
  • FLOAT/DOUBLE:浮点数,近似值存储,有精度损失
sql 复制代码
-- 金额必须用DECIMAL
amount DECIMAL(19,4)  -- 总共19位,小数4位

-- 不要用FLOAT存金额
-- 0.1 + 0.2 != 0.3 (浮点数精度问题)
面试题:DATETIME和TIMESTAMP的区别?

答案:

对比项 DATETIME TIMESTAMP
存储范围 1000-9999年 1970-2038年(MySQL 8.0已扩展)
存储空间 8字节 4字节
时区处理 存储时不变 自动转换时区
默认值 可设置 可设置CURRENT_TIMESTAMP
自动更新 不支持 支持ON UPDATE

1.2 SQL语句考点

面试题:WHERE和HAVING的区别?

答案:

  • WHERE:对原始数据过滤,在分组前执行,不能使用聚合函数
  • HAVING:对分组结果过滤,在分组后执行,可以使用聚合函数
sql 复制代码
-- WHERE:先过滤再分组
SELECT department_id, AVG(salary)
FROM employees
WHERE salary > 5000      -- 先过滤salary>5000的记录
GROUP BY department_id;

-- HAVING:先分组再过滤
SELECT department_id, AVG(salary) as avg_sal
FROM employees
GROUP BY department_id
HAVING AVG(salary) > 8000;  -- 过滤平均工资>8000的部门
面试题:IN和EXISTS的区别?

答案:

场景 推荐 原因
子表小,外表大 IN 子查询先执行,结果集小
子表大,外表小 EXISTS 外表驱动,有索引时效率高
需要判断NULL EXISTS IN对NULL处理有坑
sql 复制代码
-- IN:适合子查询结果集小
SELECT * FROM employees 
WHERE department_id IN (SELECT id FROM departments WHERE location = 'Beijing');

-- EXISTS:适合外表小,且关联列有索引
SELECT * FROM employees e
WHERE EXISTS (SELECT 1 FROM departments d WHERE d.id = e.department_id AND d.location = 'Beijing');

1.3 字符集考点

面试题:utf8和utf8mb4的区别?

答案:

  • utf8:MySQL的"utf8"实际上是utf8mb3,只支持3字节UTF-8字符(最大编码0xFFFF)
  • utf8mb4:支持4字节UTF-8字符,包括emoji、生僻汉字
sql 复制代码
-- 推荐:使用utf8mb4
CREATE TABLE users (
    name VARCHAR(100)
) DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- 排序规则选择:
-- utf8mb4_general_ci:速度快,精度略低
-- utf8mb4_unicode_ci:精度高,支持更多语言特性
-- utf8mb4_bin:二进制比较,区分大小写

二、查询篇考点

2.1 JOIN类型

面试题:各种JOIN的区别?

答案:

sql 复制代码
-- 创建示例表
CREATE TABLE A (id INT, name VARCHAR(10));
CREATE TABLE B (id INT, a_id INT, value VARCHAR(10));

INSERT INTO A VALUES (1, 'A1'), (2, 'A2'), (3, 'A3');
INSERT INTO B VALUES (1, 1, 'B1'), (2, 1, 'B2'), (3, 4, 'B3');
JOIN类型 结果 说明
INNER JOIN A1-B1, A1-B2 只返回匹配的行
LEFT JOIN A1-B1, A1-B2, A2-NULL, A3-NULL 返回左表所有行
RIGHT JOIN A1-B1, A1-B2, NULL-B3 返回右表所有行
FULL JOIN MySQL不支持,用UNION模拟 返回两边所有行
CROSS JOIN 笛卡尔积 所有组合
sql 复制代码
-- INNER JOIN
SELECT * FROM A INNER JOIN B ON A.id = B.a_id;
-- 结果:(1,A1,1,1,B1), (1,A1,2,1,B2)

-- LEFT JOIN
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id;
-- 结果:(1,A1,1,1,B1), (1,A1,2,1,B2), (2,A2,NULL,NULL,NULL), (3,A3,NULL,NULL,NULL)

-- MySQL实现FULL JOIN
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id
UNION
SELECT * FROM A RIGHT JOIN B ON A.id = B.a_id;

2.2 子查询优化

面试题:如何优化子查询?

答案:

sql 复制代码
-- 不推荐:相关子查询(每行都执行子查询)
SELECT * FROM employees e
WHERE salary > (SELECT AVG(salary) FROM employees WHERE department_id = e.department_id);

-- 推荐:派生表(子查询只执行一次)
SELECT e.* 
FROM employees e
INNER JOIN (
    SELECT department_id, AVG(salary) as avg_sal
    FROM employees
    GROUP BY department_id
) d ON e.department_id = d.department_id
WHERE e.salary > d.avg_sal;

-- 推荐:MySQL 5.7+ 使用WITH(CTE)
WITH dept_avg AS (
    SELECT department_id, AVG(salary) as avg_sal
    FROM employees
    GROUP BY department_id
)
SELECT e.* 
FROM employees e
INNER JOIN dept_avg d ON e.department_id = d.department_id
WHERE e.salary > d.avg_sal;

2.3 分页优化

面试题:大表分页如何优化?

答案:

sql 复制代码
-- 不推荐:深分页性能差
SELECT * FROM orders 
ORDER BY create_time DESC 
LIMIT 1000000, 10;
-- 需要扫描1000010行,丢弃前1000000行

-- 方案1:覆盖索引+子查询
SELECT * FROM orders o
INNER JOIN (
    SELECT order_id FROM orders 
    ORDER BY create_time DESC 
    LIMIT 1000000, 10
) tmp ON o.order_id = tmp.order_id;

-- 方案2:书签/游标分页(推荐)
-- 上一页最后一条的create_time = '2024-01-15 10:00:00'
SELECT * FROM orders 
WHERE create_time < '2024-01-15 10:00:00'
ORDER BY create_time DESC 
LIMIT 10;

-- 方案3:限制分页深度
-- 业务上只允许翻到100页以内

三、索引篇考点

3.1 B+树原理

面试题:MySQL索引为什么使用B+树?

答案:

B+树特点:

  1. 非叶子节点不存数据:只存键值和指针,一个节点可以存更多键值,树更矮
  2. 叶子节点存数据:并且叶子节点之间有指针相连,便于范围查询
  3. 所有叶子节点在同一层:查询效率稳定

为什么不用其他数据结构:

数据结构 为什么不使用
哈希表 不支持范围查询和排序
二叉树 数据量大时树太高,IO次数多
B树 非叶子节点也存数据,同样数据量树更高
跳表 范围查询效率不如B+树
复制代码
B+树结构示意:

                    [10 | 20 | 30]
                   /    |    |    \
            [1|5|9] [11|15|19] [21|25|29] [31|35|39]
            /  |  \   /  |  \   /  |  \   /  |  \
           1   5   9 11  15  19 21  25  29 31  35  39
           |   |   |  |   |   |  |   |   |  |   |   |
          数据 数据 ...

3.2 最左前缀原则

面试题:什么是最左前缀原则?

答案:

复合索引(a, b, c)的生效情况:

sql 复制代码
-- 索引生效
WHERE a = 1
WHERE a = 1 AND b = 2
WHERE a = 1 AND b = 2 AND c = 3
WHERE a = 1 AND c = 3  -- a生效,c不生效
WHERE a = 1 AND b > 2 AND c = 3  -- a,b生效,c不生效(b是范围查询)

-- 索引不生效
WHERE b = 2
WHERE b = 2 AND c = 3
WHERE c = 3

原理:B+树按照索引列的顺序排序,必须先匹配最左边的列。

3.3 索引失效场景

面试题:哪些情况会导致索引失效?

答案:

sql 复制代码
-- 1. 对索引列使用函数
WHERE YEAR(create_time) = 2023  -- 失效
WHERE create_time >= '2023-01-01' AND create_time < '2024-01-01'  -- 有效

-- 2. 隐式类型转换
WHERE phone = 13800138000  -- 失效(字符串字段用数字查)
WHERE phone = '13800138000'  -- 有效

-- 3. LIKE以通配符开头
WHERE name LIKE '%张%'  -- 失效
WHERE name LIKE '张%'   -- 有效(使用索引)

-- 4. OR条件使用不当
WHERE id = 1 OR age = 20  -- 可能失效(除非都有索引且优化器选择使用)

-- 5. 不等于、NOT IN
WHERE status != 1  -- 可能失效(取决于数据分布)
WHERE status NOT IN (1, 2)  -- 可能失效

-- 6. IS NULL(取决于数据分布和数据量)
WHERE name IS NULL  -- 可能失效

-- 7. 复合索引不满足最左前缀
-- 索引(a, b, c)
WHERE b = 2 AND c = 3  -- 失效

-- 8. 范围查询后失效
-- 索引(a, b, c)
WHERE a = 1 AND b > 2 AND c = 3  -- c不生效

3.4 覆盖索引

面试题:什么是覆盖索引?

答案:

覆盖索引:查询的所有列都在索引中,无需回表查询数据行。

sql 复制代码
-- 创建复合索引
CREATE INDEX idx_dept_name ON employees(department_id, first_name, last_name);

-- 覆盖索引查询(Extra: Using index)
SELECT first_name, last_name 
FROM employees 
WHERE department_id = 10;
-- 只需要读索引,不需要回表

-- 非覆盖索引查询(需要回表)
SELECT first_name, last_name, email  -- email不在索引中
FROM employees 
WHERE department_id = 10;
-- 先查索引找到主键,再回表查email

四、事务篇考点

4.1 ACID特性

面试题:事务的ACID特性是什么?

答案:

特性 说明 MySQL实现
Atomicity(原子性) 事务要么全成功,要么全失败 undo log
Consistency(一致性) 事务前后数据完整性约束不被破坏 约束检查+其他特性保证
Isolation(隔离性) 事务之间相互隔离 锁 + MVCC
Durability(持久性) 事务提交后数据永久保存 redo log

4.2 隔离级别

面试题:四种隔离级别及问题?

答案:

隔离级别 脏读 不可重复读 幻读
READ UNCOMMITTED 可能 可能 可能
READ COMMITTED 不会 可能 可能
REPEATABLE READ(默认) 不会 不会 可能(InnoDB不会)
SERIALIZABLE 不会 不会 不会
sql 复制代码
-- 查看隔离级别
SELECT @@transaction_isolation;  -- MySQL 8.0
SELECT @@tx_isolation;           -- MySQL 5.7

-- 设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

4.3 MVCC机制

面试题:什么是MVCC?

答案:

MVCC(Multi-Version Concurrency Control):多版本并发控制,通过保存数据的历史版本,实现读写不阻塞。

实现原理:

sql 复制代码
-- 每行记录隐藏字段
DB_TRX_ID:最后修改事务ID
DB_ROLL_PTR:回滚指针,指向undo log
DB_ROW_ID:行ID(无主键时自动生成)

-- Read View(读视图)
在RC和RR级别下,SELECT时生成Read View,判断数据可见性

版本链:

复制代码
当前行数据 <- undo log <- undo log <- undo log
(最新)    (上个版本)  (更早版本)  (最初版本)

判断规则:

  1. DB_TRX_ID < min_trx_id:已提交,可见
  2. DB_TRX_ID >= max_trx_id:将来事务,不可见
  3. min_trx_id <= DB_TRX_ID < max_trx_id:
    • 在m_ids列表中:未提交,不可见
    • 不在m_ids列表中:已提交,可见

4.4 幻读解决

面试题:InnoDB如何解决幻读?

答案:

幻读:同一事务两次查询,第二次查到了第一次没有的行。

解决方案:

sql 复制代码
-- 方案1:快照读(普通SELECT)
-- MVCC保证可重复读,但可能幻读
SELECT * FROM orders WHERE amount > 100;

-- 方案2:当前读(加锁读)
-- 使用间隙锁(Gap Lock)解决幻读
SELECT * FROM orders WHERE amount > 100 FOR UPDATE;
SELECT * FROM orders WHERE amount > 100 LOCK IN SHARE MODE;

-- 间隙锁锁定范围,阻止其他事务插入

注意:InnoDB在RR级别下,当前读使用间隙锁解决幻读;快照读使用MVCC,但严格来说RR的快照读也可能出现幻读(特殊场景)。


五、锁篇考点

5.1 锁的分类

面试题:MySQL有哪些锁?

答案:

按粒度分:

锁类型 粒度 开销 并发度 适用引擎
行锁 InnoDB
表锁 MyISAM、InnoDB
页锁 BDB(已淘汰)

按功能分:

锁类型 说明
共享锁(S锁) 读锁,多个事务可同时持有
排他锁(X锁) 写锁,只能一个事务持有
意向共享锁(IS) 表级锁,表示事务将获取行级S锁
意向排他锁(IX) 表级锁,表示事务将获取行级X锁

InnoDB行锁算法:

锁算法 说明
Record Lock 锁定单个记录
Gap Lock 锁定范围,不包括记录本身(防幻读)
Next-Key Lock Record Lock + Gap Lock,锁定记录及范围

5.2 死锁

面试题:什么是死锁?如何解决?

答案:

死锁:两个或多个事务相互等待对方释放锁,形成循环等待。

sql 复制代码
-- 死锁示例
-- 事务A:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;  -- 锁id=1
UPDATE accounts SET balance = balance + 100 WHERE id = 2;  -- 等待id=2

-- 事务B:
BEGIN;
UPDATE accounts SET balance = balance - 100 WHERE id = 2;  -- 锁id=2
UPDATE accounts SET balance = balance + 100 WHERE id = 1;  -- 等待id=1
-- 死锁!

解决方案:

sql 复制代码
-- 1. 查看死锁日志
SHOW ENGINE INNODB STATUS;  -- 查看LATEST DETECTED DEADLOCK

-- 2. 设置死锁检测超时
SET innodb_lock_wait_timeout = 50;  -- 默认50秒

-- 3. 预防死锁
-- a) 按固定顺序访问资源
-- b) 尽量缩短事务长度
-- c) 使用较低的隔离级别
-- d) 使用乐观锁代替悲观锁

-- 乐观锁示例
UPDATE accounts 
SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 1;
-- 如果version变了,更新失败,业务层重试

5.3 乐观锁和悲观锁

面试题:乐观锁和悲观锁的区别?

答案:

对比项 乐观锁 悲观锁
思想 认为不会冲突,提交时检查 认为会冲突,先加锁
实现 版本号、CAS SELECT FOR UPDATE
适用场景 读多写少 写多读少
性能 高并发时可能重试 锁等待开销
复杂度 业务层实现 数据库支持
sql 复制代码
-- 悲观锁
BEGIN;
SELECT * FROM inventory WHERE product_id = 100 FOR UPDATE;
-- 执行业务逻辑
UPDATE inventory SET count = count - 1 WHERE product_id = 100;
COMMIT;

-- 乐观锁
-- 表结构增加version字段
UPDATE inventory 
SET count = count - 1, version = version + 1 
WHERE product_id = 100 AND version = #{version};
-- 如果更新行数为0,说明数据被修改,业务层重试

六、优化篇考点

6.1 慢查询分析

面试题:如何定位慢SQL?

答案:

sql 复制代码
-- 1. 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

-- 2. 查看正在执行的慢查询
SHOW PROCESSLIST;

-- 3. 使用performance_schema
SELECT * FROM performance_schema.events_statements_history_long 
WHERE TIMER_WAIT > 1 * 10^12  -- 超过1秒
ORDER BY TIMER_WAIT DESC;

-- 4. 使用EXPLAIN分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 100;

-- 关注:
-- type:访问类型,ALL表示全表扫描
-- key:使用的索引
-- rows:扫描行数
-- Extra:额外信息,Using filesort、Using temporary要警惕

6.2 EXPLAIN详解

面试题:EXPLAIN的字段含义?

答案:

字段 说明 注意点
id 执行顺序 id相同从上到下,id不同从大到小
select_type 查询类型 SIMPLE、PRIMARY、SUBQUERY、DERIVED等
table 访问的表 可能是别名或派生表
type 访问类型 system>const>eq_ref>ref>range>index>ALL
possible_keys 可能使用的索引 -
key 实际使用的索引 NULL表示没用索引
key_len 索引长度 越短越好
ref 与索引比较的列 -
rows 估算扫描行数 越小越好
filtered 过滤比例 百分比
Extra 额外信息 Using index、Using where、Using filesort等

type从优到劣:

复制代码
system > const > eq_ref > ref > fulltext > ref_or_null > 
index_merge > unique_subquery > index_subquery > 
range > index > ALL

-- 至少要达到range,最好达到ref

6.3 大表优化

面试题:大表如何优化?

答案:

sql 复制代码
-- 1. 优化SQL和索引(首要)
-- 添加合适的索引
CREATE INDEX idx_user_time ON orders(user_id, create_time);

-- 2. 垂直拆分
-- 将大字段拆分到扩展表
CREATE TABLE user_profiles (
    user_id BIGINT PRIMARY KEY,
    bio TEXT,
    avatar_url VARCHAR(500)
);

-- 3. 水平拆分/分区
-- 按时间分区
CREATE TABLE orders_2024 PARTITION BY RANGE (MONTH(create_time)) (
    PARTITION p1 VALUES LESS THAN (2),
    PARTITION p2 VALUES LESS THAN (3),
    ...
);

-- 4. 读写分离
-- 主库写,从库读

-- 5. 使用缓存
-- 热点数据放入Redis

-- 6. 归档历史数据
-- 将冷数据移到历史表或数据仓库

七、架构篇考点

7.1 主从复制

面试题:主从复制的原理?

答案:

复制代码
主从复制流程:

1. Master写入数据,记录binlog(二进制日志)
2. Slave的IO线程连接Master,请求binlog
3. Master的Dump线程发送binlog给Slave
4. Slave的IO线程将binlog写入relay log(中继日志)
5. Slave的SQL线程读取relay log,重放SQL

Master                      Slave
  |                           |
  |  1. 写数据,记binlog       |
  |-------------------------->|
  |                           |
  |  2. IO线程请求binlog       |
  |<--------------------------|
  |                           |
  |  3. Dump线程发送binlog     |
  |-------------------------->|
  |                           |
  |                           | 4. 写入relay log
  |                           | 5. SQL线程重放

复制模式:

  • 异步复制:Master不等待Slave确认,性能最好,可能丢数据
  • 半同步复制:Master等待至少一个Slave确认,平衡方案
  • 组复制:多数派确认,强一致性

7.2 读写分离

面试题:如何实现读写分离?

答案:

方案1:应用层实现

java 复制代码
// Spring Boot动态数据源
@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource routingDataSource() {
        DynamicRoutingDataSource routingDataSource = new DynamicRoutingDataSource();
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put("master", masterDataSource());
        targetDataSources.put("slave", slaveDataSource());
        routingDataSource.setTargetDataSources(targetDataSources);
        return routingDataSource;
    }
}

@Service
public class OrderService {
    @Transactional  // 默认走主库
    public void createOrder(Order order) { ... }
    
    @Transactional(readOnly = true)  // 走从库
    public Order getOrder(Long id) { ... }
}

方案2:中间件实现

  • MyCat
  • ShardingSphere
  • Atlas

方案3:DNS/VIP

  • 写域名指向Master
  • 读域名指向Slave集群

7.3 分库分表

面试题:什么时候需要分库分表?

答案:

分库时机:

  • 单库数据量超过500GB
  • 单库QPS超过10000
  • 需要隔离不同业务数据

分表时机:

  • 单表数据量超过1000万
  • 单表大小超过10GB
  • 索引无法完全放入内存

分片策略:

sql 复制代码
-- 1. 取模分片(适合均匀分布)
-- user_id % 4 决定分到哪个库/表

-- 2. 范围分片(适合时间序列)
-- 按时间分表:orders_202401, orders_202402

-- 3. 哈希分片(适合避免热点)
-- 对user_id做哈希后取模

-- 4. 标签分片(适合按业务)
-- 按地区:user_north, user_south

分库分表问题:

问题 解决方案
跨库JOIN 避免JOIN,数据冗余或应用层组装
跨库事务 分布式事务(Seata)、最终一致性
全局ID 雪花算法、号段模式
分页排序 归并排序、限制深度
数据迁移 双写、增量同步

八、高频SQL手写题

8.1 TopN问题

题目:查询每个部门工资最高的前3名员工

sql 复制代码
-- 方案1:窗口函数(MySQL 8.0+)
SELECT *
FROM (
    SELECT 
        *,
        DENSE_RANK() OVER (PARTITION BY department_id ORDER BY salary DESC) as rank_num
    FROM employees
) t
WHERE rank_num <= 3;

-- 方案2:关联子查询(兼容旧版本)
SELECT e.*
FROM employees e
WHERE (
    SELECT COUNT(DISTINCT e2.salary)
    FROM employees e2
    WHERE e2.department_id = e.department_id
    AND e2.salary > e.salary
) < 3;

8.2 连续登录问题

题目:查询连续登录3天以上的用户

sql 复制代码
-- 表结构:user_login(user_id, login_date)

-- 解题思路:
-- 1. 去重登录日期
-- 2. 按用户分组,日期排序
-- 3. 计算日期与排序的差值(连续日期差值相同)
-- 4. 分组统计连续天数

WITH login_with_rank AS (
    SELECT 
        user_id,
        login_date,
        DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) as grp
    FROM (SELECT DISTINCT user_id, login_date FROM user_login) t
)
SELECT user_id, COUNT(*) as continuous_days
FROM login_with_rank
GROUP BY user_id, grp
HAVING COUNT(*) >= 3;

8.3 行列转换

题目:将行转列,统计每个用户各月份的订单金额

sql 复制代码
-- 源数据
-- user_id | month | amount
-- 1       | 1     | 100
-- 1       | 2     | 200
-- 2       | 1     | 150

-- 目标结果
-- user_id | Jan | Feb | Mar | ...
-- 1       | 100 | 200 | 0   | ...
-- 2       | 150 | 0   | 0   | ...

-- 行转列
SELECT 
    user_id,
    SUM(CASE WHEN month = 1 THEN amount ELSE 0 END) as Jan,
    SUM(CASE WHEN month = 2 THEN amount ELSE 0 END) as Feb,
    SUM(CASE WHEN month = 3 THEN amount ELSE 0 END) as Mar,
    -- ...
FROM orders
GROUP BY user_id;

-- 列转行(反向操作)
-- 使用UNION ALL

8.4 中位数计算

题目:查询每个部门工资的中位数

sql 复制代码
-- MySQL 8.0+ 窗口函数
WITH ranked AS (
    SELECT 
        department_id,
        salary,
        ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary) as rn,
        COUNT(*) OVER (PARTITION BY department_id) as cnt
    FROM employees
)
SELECT department_id, AVG(salary) as median_salary
FROM ranked
WHERE rn IN (FLOOR((cnt + 1) / 2), CEIL((cnt + 1) / 2))
GROUP BY department_id;

8.5 树形结构查询

题目:查询部门层级结构(递归CTE)

sql 复制代码
-- 表结构:departments(id, name, parent_id)

-- MySQL 8.0+ 递归CTE
WITH RECURSIVE dept_tree AS (
    -- 锚点:顶级部门
    SELECT id, name, parent_id, 0 as level, CAST(name AS CHAR(255)) as path
    FROM departments
    WHERE parent_id IS NULL
    
    UNION ALL
    
    -- 递归:子部门
    SELECT d.id, d.name, d.parent_id, dt.level + 1, CONCAT(dt.path, ' > ', d.name)
    FROM departments d
    INNER JOIN dept_tree dt ON d.parent_id = dt.id
)
SELECT * FROM dept_tree;

九、学习路线总结

9.1 知识体系图谱

复制代码
MySQL知识体系
│
├── 基础篇
│   ├── 数据类型(INT/VARCHAR/DECIMAL/DATETIME)
│   ├── SQL语句(CRUD、JOIN、子查询)
│   ├── 字符集(utf8mb4)
│   └── 存储引擎(InnoDB vs MyISAM)
│
├── 进阶篇
│   ├── 索引(B+树、最左前缀、覆盖索引)
│   ├── 事务(ACID、隔离级别、MVCC)
│   ├── 锁(行锁、表锁、死锁、乐观/悲观锁)
│   └── 日志(binlog、redo log、undo log)
│
├── 实战篇
│   ├── 视图与触发器
│   ├── 性能优化(慢查询、EXPLAIN、SQL优化)
│   ├── 备份恢复(mysqldump、xtrabackup)
│   └── 主从复制与读写分离
│
└── 架构篇
    ├── 分库分表
    ├── 高可用架构
    └── 监控与运维

9.2 学习建议

阶段 目标 时间 重点
入门 掌握基础SQL 1-2周 CRUD、简单查询
进阶 理解原理 2-4周 索引、事务、锁
实战 解决问题 4-8周 优化、备份、架构
精通 源码级理解 持续 内核、源码

9.3 推荐书籍

书籍 难度 适合阶段
《MySQL必知必会》 入门 初学者
《高性能MySQL(第4版)》 进阶 有一定基础
《MySQL技术内幕:InnoDB存储引擎》 高级 深入原理
《MySQL运维内参》 高级 DBA方向

十、参考资料

  1. MySQL 8.0 Reference Manual
  2. MySQL技术内幕:InnoDB存储引擎

互动话题

系列完结,感谢陪伴!

  1. 这15天的学习,你最大的收获是什么?
  2. 还有哪些MySQL知识点想深入了解?
  3. 你在面试中遇到过哪些印象深刻的MySQL问题?
  4. 对这个系列有什么建议或期待?

欢迎在评论区留言,我会持续关注和回复。如果觉得这个系列对你有帮助,别忘了点赞收藏,分享给更多需要的朋友!

后续计划:

  • 根据大家反馈,可能会推出进阶系列(源码分析、内核原理)
  • 或者推出其他技术栈的系列教程

敬请期待!


本文是MySQL全面教学系列第15篇,也是最后一篇。15天的陪伴,感谢有你!

系列完结,撒花!

相关推荐
拽着尾巴的鱼儿2 小时前
springboot openfeign 自定义feign 接口重试机制
java·spring boot·后端
Ceelog2 小时前
久坐党自救指南:屏幕前 8 小时,身体到底在经历什么
前端·后端
凯瑟琳.奥古斯特2 小时前
高阶子查询题目精炼
开发语言·数据库·python·职场和发展·数据库开发
身如柳絮随风扬2 小时前
数据库读写分离:从原理到实战,构建高并发系统
数据库·mysql
swipe3 小时前
DeepAgents 实战:用多 Agent 架构搭一个深度调研助手
javascript·面试·llm
XS0301063 小时前
并发编程 六
java·后端
提笔了无痕3 小时前
RAG存储策略中.md格式的切片与存储怎么处理
数据库·ai·rag
陳土3 小时前
DuckDB精读——基于Getting started with DuckDB
数据库·oracle