1. MySQL SQL基础练习的价值与意义
对于任何想要掌握数据库操作的数据从业者来说,SQL基础练习都是必经之路。我见过太多新手在面试时被简单的JOIN操作难倒,也见过不少工作两三年的开发还在用最原始的方式处理数据。100道基础练习题的价值,就在于它能系统性地覆盖SQL操作的各个核心场景。
这些练习题目通常会从最简单的SELECT查询开始,逐步深入到多表连接、子查询、聚合函数等复杂操作。通过反复练习,你不仅能记住语法,更重要的是培养"数据思维"——知道如何将一个业务问题转化为SQL查询的能力。这种能力在数据分析、后端开发、数据仓库等岗位都是核心竞争力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 练习环境准备
2.1 MySQL安装与配置
工欲善其事,必先利其器。我推荐使用MySQL 8.0社区版作为练习环境,它不仅免费,还包含了许多新特性。在Windows上可以直接下载MSI安装包,Linux用户可以通过apt或yum安装。安装完成后,记得运行mysql_secure_installation进行基本安全配置。
注意:练习时建议创建一个专门的测试数据库,不要使用生产环境。可以使用以下命令创建:
sql复制CREATE DATABASE practice_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
2.2 练习数据准备
好的练习题需要有结构合理的数据集。我建议从经典的"学生-课程-成绩"模型开始:
sql复制CREATE TABLE students (
student_id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
gender CHAR(1),
enrollment_date DATE
);
CREATE TABLE courses (
course_id INT PRIMARY KEY,
course_name VARCHAR(100) NOT NULL,
credit INT
);
CREATE TABLE scores (
id INT AUTO_INCREMENT PRIMARY KEY,
student_id INT,
course_id INT,
score DECIMAL(5,2),
FOREIGN KEY (student_id) REFERENCES students(student_id),
FOREIGN KEY (course_id) REFERENCES courses(course_id)
);
这套数据模型虽然简单,但能覆盖大多数基础SQL场景。你可以根据需要添加更多表和字段来增加复杂度。
3. 基础查询练习
3.1 单表查询基础
我们从最简单的SELECT开始,这是所有SQL查询的基础:
sql复制-- 查询所有学生信息
SELECT * FROM students;
-- 查询特定列
SELECT student_id, name FROM students;
-- 使用WHERE条件过滤
SELECT * FROM students WHERE gender = 'F';
-- 使用ORDER BY排序
SELECT * FROM students ORDER BY enrollment_date DESC;
这些看似简单的查询,其实包含了SQL最核心的逻辑:选择(Selection)、投影(Projection)和排序(Ordering)。我建议在练习时不仅要写出正确的SQL,还要思考MySQL是如何执行这些操作的。
3.2 聚合函数与分组
统计和分析是SQL的重要功能,这离不开聚合函数:
sql复制-- 计算学生总数
SELECT COUNT(*) FROM students;
-- 计算男女学生人数
SELECT gender, COUNT(*)
FROM students
GROUP BY gender;
-- 计算每门课程的平均分
SELECT c.course_name, AVG(s.score) as avg_score
FROM courses c
JOIN scores s ON c.course_id = s.course_id
GROUP BY c.course_id;
常见的聚合函数包括COUNT、SUM、AVG、MAX、MIN等。GROUP BY是SQL中最容易出错的地方之一,要记住:SELECT子句中非聚合的列必须出现在GROUP BY中。
4. 多表连接查询
4.1 各种JOIN的区别
多表连接是SQL的核心难点,也是面试中最常被问到的部分。我们先来看几种JOIN的区别:
sql复制-- INNER JOIN (只返回匹配的行)
SELECT s.name, c.course_name, sc.score
FROM students s
INNER JOIN scores sc ON s.student_id = sc.student_id
INNER JOIN courses c ON sc.course_id = c.course_id;
-- LEFT JOIN (返回左表所有行,右表不匹配则为NULL)
SELECT s.name, c.course_name, sc.score
FROM students s
LEFT JOIN scores sc ON s.student_id = sc.student_id
LEFT JOIN courses c ON sc.course_id = c.course_id;
-- RIGHT JOIN (返回右表所有行,左表不匹配则为NULL)
-- FULL OUTER JOIN (MySQL不支持,需用UNION实现)
在实际工作中,INNER JOIN和LEFT JOIN使用最频繁。我个人的经验法则是:当你想确保两边都有数据时用INNER JOIN,想保留主表所有记录时用LEFT JOIN。
4.2 子查询应用
子查询是构建复杂查询的强大工具:
sql复制-- 查询成绩高于平均分的学生
SELECT s.name, sc.score
FROM students s
JOIN scores sc ON s.student_id = sc.student_id
WHERE sc.score > (SELECT AVG(score) FROM scores);
-- 使用IN的子查询
SELECT name
FROM students
WHERE student_id IN (
SELECT student_id
FROM scores
WHERE score > 90
);
子查询可以出现在SELECT、FROM、WHERE等多个子句中。性能上要注意:相关子查询(引用外部查询列)通常比非相关子查询慢,在大数据量时要谨慎使用。
5. 高级查询技巧
5.1 窗口函数
MySQL 8.0引入了强大的窗口函数,这是数据分析的利器:
sql复制-- 计算每个学生的成绩排名
SELECT
s.name,
c.course_name,
sc.score,
RANK() OVER (PARTITION BY sc.course_id ORDER BY sc.score DESC) as rank_in_course
FROM students s
JOIN scores sc ON s.student_id = sc.student_id
JOIN courses c ON sc.course_id = c.course_id;
-- 计算移动平均
SELECT
student_id,
score_date,
score,
AVG(score) OVER (PARTITION BY student_id ORDER BY score_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as moving_avg
FROM scores;
常用的窗口函数包括ROW_NUMBER、RANK、DENSE_RANK、LEAD、LAG等。它们能在不减少行数的情况下进行计算,非常适合分析场景。
5.2 公用表表达式(CTE)
CTE能让复杂查询更清晰:
sql复制-- 查询每门课程的前三名学生
WITH course_top AS (
SELECT
course_id,
student_id,
score,
RANK() OVER (PARTITION BY course_id ORDER BY score DESC) as rank_num
FROM scores
)
SELECT
c.course_name,
s.name,
ct.score
FROM course_top ct
JOIN students s ON ct.student_id = s.student_id
JOIN courses c ON ct.course_id = c.course_id
WHERE ct.rank_num <= 3;
CTE不仅提高了可读性,还能实现递归查询等复杂功能。对于多层嵌套的子查询,改用CTE通常会让代码更易维护。
6. 性能优化与最佳实践
6.1 索引使用原则
没有索引的SQL就像没有地图的旅行。在练习时就要养成关注性能的习惯:
sql复制-- 为常用查询条件创建索引
ALTER TABLE scores ADD INDEX idx_student_course (student_id, course_id);
-- 使用EXPLAIN分析查询
EXPLAIN SELECT * FROM students WHERE name LIKE '张%';
索引原则:
- 为WHERE、JOIN、ORDER BY的列建索引
- 遵循最左前缀原则
- 避免过度索引,因为会影响写入性能
- 字符串列考虑前缀索引
6.2 查询优化技巧
一些简单但有效的优化技巧:
sql复制-- 避免SELECT *,只查询需要的列
SELECT student_id, name FROM students;
-- 使用LIMIT限制结果集
SELECT * FROM students ORDER BY enrollment_date DESC LIMIT 10;
-- 批量插入代替单条插入
INSERT INTO students (student_id, name) VALUES
(1, '张三'),
(2, '李四'),
(3, '王五');
在真实工作中,我见过一个SELECT *查询拖垮整个系统的案例。养成好的SQL习惯要从基础练习开始。
7. 常见问题与解决方案
7.1 编码问题
MySQL的编码问题困扰过每个初学者:
sql复制-- 创建数据库时指定utf8mb4
CREATE DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 连接后设置编码
SET NAMES utf8mb4;
utf8mb4是真正的UTF-8编码,支持emoji等特殊字符。记住:MySQL的utf8其实是阉割版的UTF-8,最多只支持3字节字符。
7.2 分组查询的坑
GROUP BY有几个常见陷阱:
sql复制-- 错误示例:SELECT中出现非聚合列且不在GROUP BY中
SELECT student_id, name, COUNT(*)
FROM students
GROUP BY student_id;
-- 正确写法
SELECT student_id, name, COUNT(*)
FROM students
GROUP BY student_id, name;
-- 使用HAVING过滤分组结果
SELECT course_id, AVG(score) as avg_score
FROM scores
GROUP BY course_id
HAVING avg_score > 80;
记住WHERE和HAVING的区别:WHERE在分组前过滤行,HAVING在分组后过滤组。
8. 100道练习题精选
8.1 基础题目示例
- 查询所有女生的信息
- 查询名字以"张"开头的学生
- 查询入学时间最早的前10名学生
- 查询没有成绩记录的学生
- 查询每门课程的选修人数
8.2 中级题目示例
- 查询每门课程成绩最高的学生信息
- 查询至少选修了3门课程的学生
- 查询所有课程成绩都在90分以上的学生
- 查询每门课程的成绩分布(优秀/良好/及格等)
- 查询连续两年都有成绩记录的学生
8.3 高级题目示例
- 使用递归CTE查询课程之间的先修关系
- 使用窗口函数计算每个学生的成绩百分位
- 使用JSON函数处理动态属性
- 使用存储过程批量生成测试数据
- 使用触发器实现成绩变更审计
9. 学习路径建议
根据我多年的SQL教学经验,建议按以下顺序练习:
- 单表查询(20题):掌握SELECT, WHERE, ORDER BY, LIMIT
- 聚合与分组(20题):GROUP BY, HAVING, 聚合函数
- 多表连接(20题):各种JOIN, 子查询
- 数据修改(10题):INSERT, UPDATE, DELETE
- 高级特性(20题):窗口函数, CTE, JSON处理
- 性能优化(10题):EXPLAIN, 索引优化
每周完成一个类别的练习,并复习之前的题目。坚持6周,你的SQL水平会有质的飞跃。
10. 资源推荐
除了这100道练习题,我还推荐以下资源:
- MySQL官方文档:最权威的参考
- LeetCode数据库题目:实战性强的题目
- SQLZoo:交互式学习平台
- "SQL进阶教程":深入讲解高级特性
- 数据库设计范式:理解背后的理论
记住:SQL是一门实践性很强的技能,只看不练永远学不会。建议每天至少花1小时实际编写和调试SQL语句。
