目录
- 一、调优基本功:看懂SQL的"体检报告"
- 1.1 给SQL做体检(执行计划分析)
- 1.2 给数据表"建索引"的正确姿势
- 二、不改SQL也能提速的秘籍
- 2.1 分区表:把大象切成牛排
- 2.2 物化视图:给常用查询拍快照
- 三、SQL改写:让你的查询脱胎换骨
- 3.1 告别全表扫描:日期查询优化
- 3.2 子查询转JOIN:速度提升10倍
- 四、高级SQL:让你的代码更优雅
- 4.1 分组统计绝招(GROUP BY扩展)
- 4.2 MERGE语句:智能更新插入
- 五、实战训练场
- 5.1 调优练习题
- 5.2 参考答案
- 六、学习路线图
一、调优基本功:看懂SQL的"体检报告"
1.1 给SQL做体检(执行计划分析)
场景:就像看病要先拍X光,调优前先看执行计划
-- 查看执行计划(以MySQL为例)
EXPLAIN SELECT * FROM 用户表 WHERE 年龄 > 18;
执行计划关键指标:
指标 | 说明 | 类比 |
---|---|---|
type | 查询类型 | 坐电梯(快) vs 爬楼梯(慢) |
rows | 扫描行数 | 需要翻找的抽屉数量 |
Extra | 附加信息 | 体检报告备注栏 |
1.2 给数据表"建索引"的正确姿势
经典误区:索引越多越好?错!就像图书馆的书架标签太多反而更难找
创建索引的正确方式:
-- 给高频查询字段建索引(比如手机号)
CREATE INDEX idx_手机号 ON 用户表(手机号);
-- 联合索引就像多级目录
CREATE INDEX idx_城市_年龄 ON 用户表(城市, 年龄);
索引类型选择指南:
场景 | 推荐索引类型 | 类比 |
---|---|---|
精确查询(手机号) | B树索引 | 精确到页码的目录 |
大量重复值(性别) | 位图索引 | 颜色分类标签 |
模糊查询(姓名%) | 全文索引 | 关键词检索 |
二、不改SQL也能提速的秘籍
2.1 分区表:把大象切成牛排
原理:把大表按规则分割,就像把衣柜分季节整理
-- 创建按月分区的订单表
CREATE TABLE 订单表 (
订单ID INT,
金额 DECIMAL,
下单时间 DATE
) PARTITION BY RANGE (YEAR_MONTH(下单时间)) (
PARTITION p202301 VALUES LESS THAN (202302),
PARTITION p202302 VALUES LESS THAN (202303)
);
2.2 物化视图:给常用查询拍快照
-- 创建每日销售额快照
CREATE MATERIALIZED VIEW 每日销售
REFRESH FAST ON COMMIT
AS
SELECT 日期, SUM(金额)
FROM 订单
GROUP BY 日期;
适用场景:报表统计、实时性要求不高的看板
三、SQL改写:让你的查询脱胎换骨
3.1 告别全表扫描:日期查询优化
错误示范:
SELECT * FROM 订单 WHERE YEAR(下单时间)=2023; -- 全表扫描!
正确姿势:
SELECT * FROM 订单
WHERE 下单时间 >= '2023-01-01'
AND 下单时间 < '2024-01-01'; -- 命中索引!
原理:就像查2023年的日记,直接找对应年份的本子,而不是每页都看
3.2 子查询转JOIN:速度提升10倍
低效写法:
SELECT * FROM 用户
WHERE 城市 IN (SELECT 城市 FROM 活跃城市);
高效改写:
SELECT 用户.*
FROM 用户
JOIN 活跃城市 ON 用户.城市 = 活跃城市.城市;
对比:
方式 | 执行时间 | 资源消耗 | 类比 |
---|---|---|---|
子查询 | 2.3s | 高 | 先列清单再逐个找 |
JOIN | 0.2s | 低 | 直接对照地图找 |
四、高级SQL:让你的代码更优雅
4.1 分组统计绝招(GROUP BY扩展)
ROLLUP分组:生成小计和总计
-- 统计各城市+各年龄段的消费总额
SELECT 城市, 年龄段, SUM(消费金额)
FROM 用户
GROUP BY ROLLUP(城市, 年龄段);
输出结果示例:
城市 | 年龄段 | SUM(消费金额) |
---|---|---|
北京 | 20-30 | 150000 |
北京 | NULL | 300000 |
NULL | NULL | 950000 |
4.2 MERGE语句:智能更新插入
MERGE INTO 员工表 AS target
USING (SELECT * FROM 新员工表) AS source
ON (target.工号 = source.工号)
WHEN MATCHED THEN
UPDATE SET target.工资 = source.工资
WHEN NOT MATCHED THEN
INSERT (工号, 姓名, 工资)
VALUES (source.工号, source.姓名, source.工资);
应用场景:数据同步、ETL处理
五、实战训练场
5.1 调优练习题
-- 原始语句(执行慢)
SELECT * FROM 订单
WHERE 用户ID IN (
SELECT 用户ID FROM 用户
WHERE 注册时间 > '2020-01-01'
)
ORDER BY 下单时间 DESC;
-- 请优化此SQL(答案见文末)
5.2 参考答案
方案1:使用JOIN优化
SELECT o.*
FROM 订单 o
JOIN 用户 u ON o.用户ID = u.用户ID
WHERE u.注册时间 > '2020-01-01'
ORDER BY o.下单时间 DESC;
方案2:使用EXISTS
SELECT * FROM 订单 o
WHERE EXISTS (
SELECT 1 FROM 用户 u
WHERE u.用户ID = o.用户ID
AND u.注册时间 > '2020-01-01'
)
ORDER BY 下单时间 DESC;
六、学习路线图
阶段 | 推荐学习内容 | 学习资源 |
---|---|---|
新手村 | 基础查询、简单过滤 | 《SQL必知必会》 |
进阶之路 | 索引优化、复杂JOIN | 极客时间《SQL必知必会》 |
高手殿堂 | 执行计划分析、分布式SQL | Oracle官方文档 |
宗师境界 | 数据库内核原理 | 《数据库系统实现》 |
🎯下期预告:《SQL优化之分析函数》
💬互动话题:你在学习SQL时遇到过哪些坑?欢迎评论区留言讨论!
🏷️温馨提示:我是[随缘而动,随遇而安], 一个喜欢用生活案例讲技术的开发者。如果觉得有帮助,点赞关注不迷路🌟