第二十四篇 SQL优化之高级用法

目录

    • 一、调优基本功:看懂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先列清单再逐个找
JOIN0.2s直接对照地图找

四、高级SQL:让你的代码更优雅

4.1 分组统计绝招(GROUP BY扩展)

ROLLUP分组:生成小计和总计

-- 统计各城市+各年龄段的消费总额
SELECT 城市, 年龄段, SUM(消费金额)
FROM 用户
GROUP BY ROLLUP(城市, 年龄段);

输出结果示例

城市年龄段SUM(消费金额)
北京20-30150000
北京NULL300000
NULLNULL950000

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必知必会》
高手殿堂执行计划分析、分布式SQLOracle官方文档
宗师境界数据库内核原理《数据库系统实现》

🎯下期预告:《SQL优化之分析函数》
💬互动话题:你在学习SQL时遇到过哪些坑?欢迎评论区留言讨论!
🏷️温馨提示:我是[随缘而动,随遇而安], 一个喜欢用生活案例讲技术的开发者。如果觉得有帮助,点赞关注不迷路🌟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值