SQL 查询优化助手
把"跑 30 秒的查询"变成"跑 0.3 秒",靠的是步骤不是玄学。
适用场景
- 报表/接口查询越来越慢,DBA 没空接,自己想先定位问题。
- 新写的 SQL 上线后拖垮整体性能,需要快速改写。
- 你想建立一套"看到慢 SQL 就知道往哪查"的肌肉记忆。
核心纪律
- 先量后改。 永远先拿执行计划(EXPLAIN)看瓶颈,不要凭直觉加索引。
- 索引是第一杠杆。 80% 的慢查询靠"该有索引没索引"或"索引没用上"解决。
- 避免 SELECT *。只取需要的列,减少 IO 与回表。
- 函数别包列。
WHERE DATE(created_at)=...会让索引失效,改成范围比较。 - 大表慎用 IN/NOT IN。 长列表用 EXISTS/JOIN 替代,防膨胀。
六步优化法
- 抓慢 SQL:从慢查询日志/监控拿到目标 SQL 与耗时、扫描行数。
- 看执行计划:
EXPLAIN(MySQL)/EXPLAIN ANALYZE(PG)看 type/rows/key,定位 ALL(全表扫)与 Using filesort。 - 查索引覆盖:确认 WHERE/JOIN/ORDER BY 列是否有索引;缺失则建;有但没用上则查是否函数包列、隐式类型转换。
- 改写反模式:SELECT *→指定列;函数包列→范围;IN 长列表→JOIN/EXISTS;深分页
LIMIT 100000,20→游标/延迟关联。 - 验证对比:在同数据量下重跑,对比耗时与扫描行数,确认下降。
- 出报告:输出"原 SQL 问题点 + 改写后 SQL + 前后耗时对比 + 仍需关注的索引建议"。
示例
示例 1|全表扫描
SELECT * FROM orders WHERE DATE(created_at)='2026-08-20' 全表扫。改写:created_at >= '2026-08-20' AND created_at < '2026-08-21',并在 created_at 建索引 → 走 range,耗时 30s→0.2s。
示例 2|深分页
LIMIT 100000,20 慢。改写延迟关联:SELECT * FROM orders a JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000,20) b ON a.id=b.id → 避免读 10 万行再丢。
示例 3|IN 长列表
WHERE user_id IN (1,2,...,5000) 慢。改写 JOIN 临时表或 EXISTS 子查询 → 计划更稳定。
边界(7 条)
- 本技能给诊断步骤与改写思路,不连生产库、不自动执行 DDL,落地由用户在自己的环境操作。
- 加索引有写放大与锁表成本,大表线上加索引须用在线 DDL(如
ALGORITHM=INPLACE)并挑低峰。 - 不同数据库(MySQL/PG/SQLServer)执行计划语法不同,本文步骤通用,具体命令以对应文档为准。
- 统计信息过期会导致计划走偏,优化前先验
ANALYZE TABLE更新统计。 - 不主张为单条 SQL 过度建索引,权衡读写比。
- 涉及合规/敏感数据的查询优化,须走公司审批,不在示例写真实数据。
- "快了"最终以同口径前后对比为准,本技能保证方法可复现,不保证绝对数值。
自检清单
- [ ] 已拿执行计划并定位瓶颈(全表扫/文件排序)
- [ ] WHERE/JOIN/ORDER BY 列索引已查
- [ ] 函数包列/隐式转换已消除
- [ ] SELECT * 已改指定列
- [ ] 深分页/长 IN 已改写
- [ ] 同数据量前后耗时已对比
- [ ] 已输出优化报告