慢查询练习集

八条真慢 SQL,每条附病因、处方与前后耗时。分组抽题,20 分钟走完三步急诊—— 处方没有验证截图,一律按玄学处理,不得分。

题量 · 8 难度 · ★ → ★★★ 评分 · 提速倍数 ×60% + 讲得清 ×40%
00 / SCHEMA课堂库

先认识病人

全部题目跑在同一个脱敏副本上。行数是所有诊断的参照系。

classroom · 只读副本 行数参照系
students     12,000 行    (id, name, major, class_id, phone)
enrollments  1,204,880 行 (id, student_id, course_id, term, score)
courses          800 行    (id, title, teacher, credit)
crawl_events 2,318,442 行 (id, student_id, url, title, created_at)
01 / DRILLS八条慢 SQL

从 ★ 全班必做,到 ★★★ 抢答

每题先读 SQL 与症状,走完三步再展开答案对照。

D1 · ★等值无索引ALL
SELECT * FROM students
WHERE major = '大数据管理与应用';

type 是什么?rows 多少?怎么开方?

展开答案
判决:ALL · 12,000 行 · 0.94 s。处方:CREATE INDEX idx_major ON students (major)。验证:ref · 214 行 · 0.01 s。原理:等值条件是索引最舒服的姿势——热身题,让学生把口令说顺。
D2 · ★★不及格名单ALL
SELECT name, term, score FROM enrollments
WHERE score < 60;

type 是什么?rows 多少?怎么开方?

展开答案
判决:ALL · 1,204,880 行 · 1.83 s。处方:CREATE INDEX idx_score ON enrollments (score)。验证:range · 81,920 行 · 0.42 s。提醒:8 万次回表仍是大头——进阶方案是覆盖索引 (term, score),留给抢答。
D3 · ★★复合条件只沾一半光ref 偏慢
-- 已有索引:idx_student(student_id)
SELECT course_id, score FROM enrollments
WHERE student_id = 42 AND term = '2026-秋';

索引已经命中了吗?还有多少优化空间?

展开答案
判决:ref · 244 行(命中 student_id 后,回表再过滤 term)· 0.11 s。处方:CREATE INDEX idx_stu_term ON enrollments (student_id, term)。验证:ref · 37 行 · 0.01 s。原理:两个等值列都进索引,过滤在索引层完成。
D4 · ★★★前导通配符姿势病
-- 即便 idx_title 存在
SELECT id, title FROM courses
WHERE title LIKE '%数据库%';

索引为什么帮不上忙?

展开答案
判决:ALL · 800 行——前导 % 让 B+ 树无法定位起点,有索引也用不上。处方:业务上改成前缀匹配 LIKE '数据库%',或上全文索引。教学点:表小不慢,但姿势是错的——考试只考姿势,生产才会考规模。
D5 · ★★★类型穿错了衣服隐式转换
-- phone 是 VARCHAR(20)
SELECT * FROM students
WHERE phone = 13812345678;

列上明明有索引,为什么 key 是 NULL?

展开答案
判决:ALL · 12,000 行 · 0.92 s——字符串列拿数字查,MySQL 隐式转换,索引失效。处方:给值加上引号 WHERE phone = '13812345678'。验证:const · 1 行 · 0.00 s。一句话:给列穿对类型的外衣,索引才认。
D6 · ★★本月的采集量ALL
SELECT COUNT(*) FROM crawl_events
WHERE created_at >= '2026-11-01';

type 是什么?rows 多少?怎么开方?

展开答案
判决:ALL · 2,318,442 行 · 2.1 s。处方:CREATE INDEX idx_created ON crawl_events (created_at)。验证:range · 96,113 行 · 0.18 s。原理:时间列是报表查询最常出现的位置,值得单独一条索引。
D7 · ★★★排行榜的代价filesort
SELECT student_id, score FROM enrollments
ORDER BY score DESC
LIMIT 10;

没有 WHERE,为什么还是 1.9 秒?

展开答案
判决:ALL · 1,204,880 行 · Extra: Using filesort · 1.9 s——全表捞出来再排一遍。处方:CREATE INDEX idx_score2 ON enrollments (score)。验证:index · 索引天然有序,LIMIT 10 只摸 10 行 · 0.00 s。原理:ORDER BY 也能吃索引——全表变 10 行。
D8 · ★★★外键没索引(最像生产事故)38 s
-- 故意删掉了 enrollments.student_id 上的索引
SELECT s.name, e.score
FROM students s
JOIN enrollments e ON e.student_id = s.id
WHERE s.class_id = 3;

为什么这条要跑 38 秒?

展开答案
判决:s 侧 ref 48 行,e 侧对每一行都全表扫——48 × 1,204,880 · 38 s。处方:CREATE INDEX idx_e_student ON enrollments (student_id)。验证:0.02 s,提速近两千倍。原理:外键列没索引,JOIN 就是连环车祸。教学点:最接近真实生产事故的一题——建表评审时第一个查它。
02 / HOWTO怎么用这八题

分组急诊赛的题库

抽题
★ 两题全班必做打底;★★、★★★ 按组抽签——抽到 D8 的组又悲又喜。
限时 20 分钟
走完三步急诊:定位 → 读片 → 开方。处方与前后耗时写进对比卡。
上墙
各组提速倍数实时上墙——大屏再一次当排行榜用。
评委抽问
随机抽一组问「为什么」;答不出,提速倍数减半计分。没有验证截图的处方,直接零分。
评分项权重怎么量
提速倍数60前后耗时之比,最高计 1000 倍封顶
讲得清程度40抽问「为什么」:type / rows / Extra 说对几条
验证截图一票否决没有前后对比的处方按玄学处理,不得分
「练习集的答案不在折叠里,在 EXPLAIN 里。」 —— 每一条答案都要亲手跑出来才算数;跑出来的数字,可能和折叠里的差一点——说出为什么,比数字本身更值钱。