1. MySQL SQL练习题的价值与学习方法
作为关系型数据库的标杆产品,MySQL在实际业务中的应用场景非常广泛。但很多开发者在学习SQL时容易陷入"看得懂但写不出"的困境——这就是SQL练习题的价值所在。通过系统化的题目训练,我们能够:
- 掌握不同业务场景下的数据建模思路
- 培养复杂查询的逻辑分解能力
- 熟悉各类SQL函数的适用场景
- 理解执行计划与性能优化的关联
我建议的学习路径是:先理解题目对应的业务场景 → 分析需要使用的数据表结构 → 拆解查询逻辑步骤 → 编写并调试SQL → 最后优化查询性能。下面通过典型题目来演示这个完整过程。
2. 基础查询题目精讲
2.1 单表基础查询
假设有员工表employees:
sql复制CREATE TABLE employees (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50) NOT NULL,
dept_id INT,
salary DECIMAL(10,2),
hire_date DATE
);
题目1:查询薪资大于10000的员工姓名和部门ID
sql复制SELECT emp_name, dept_id
FROM employees
WHERE salary > 10000;
注意:比较运算符在WHERE子句中的优先级低于逻辑运算符,复杂条件建议使用括号明确优先级
题目2:查询2015年后入职的员工,按薪资降序排列
sql复制SELECT *
FROM employees
WHERE hire_date >= '2015-01-01'
ORDER BY salary DESC;
实操心得:日期比较时建议使用标准格式'YYYY-MM-DD',避免因地区设置导致的解析错误
2.2 多表连接查询
增加部门表departments:
sql复制CREATE TABLE departments (
dept_id INT PRIMARY KEY,
dept_name VARCHAR(50) NOT NULL,
location VARCHAR(100)
);
题目3:查询每个员工的姓名及其所属部门名称
sql复制SELECT e.emp_name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;
常见连接类型对比:
| 连接类型 | 关键字 | 保留哪侧数据 | 典型场景 |
|---|---|---|---|
| 内连接 | INNER JOIN | 两表匹配行 | 获取关联数据 |
| 左连接 | LEFT JOIN | 左表全部行 | 主表查询 |
| 右连接 | RIGHT JOIN | 右表全部行 | 较少使用 |
| 全连接 | FULL JOIN | 两表全部行 | 数据比对 |
3. 高级查询技巧解析
3.1 子查询应用
题目4:查询薪资高于本部门平均薪资的员工
sql复制SELECT emp_name, salary
FROM employees e
WHERE salary > (
SELECT AVG(salary)
FROM employees
WHERE dept_id = e.dept_id
);
性能提示:相关子查询会对外层查询的每行数据执行一次,大数据量时应考虑改用JOIN+GROUP BY方案
3.2 窗口函数实战
题目5:查询每个部门薪资排名前3的员工
sql复制SELECT *
FROM (
SELECT
emp_name,
dept_id,
salary,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) as rank_num
FROM employees
) t
WHERE rank_num <= 3;
窗口函数执行顺序:
- 按PARTITION BY分组
- 组内按ORDER BY排序
- 计算指定聚合函数
- 将结果附加到每行
4. 性能优化专项训练
4.1 索引使用原则
针对employees表的常见查询场景,建议创建以下索引:
sql复制-- 单列索引
CREATE INDEX idx_dept ON employees(dept_id);
CREATE INDEX idx_salary ON employees(salary);
-- 复合索引
CREATE INDEX idx_dept_salary ON employees(dept_id, salary);
索引选择策略:
| 查询条件 | 推荐索引 | 原因 |
|---|---|---|
| WHERE dept_id = ? | idx_dept | 精确匹配单列 |
| WHERE salary > ? | idx_salary | 范围查询 |
| WHERE dept_id = ? ORDER BY salary | idx_dept_salary | 避免filesort |
4.2 EXPLAIN实战分析
对题目3的查询进行性能分析:
sql复制EXPLAIN SELECT e.emp_name, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;
典型执行计划解读:
| 列名 | 值 | 含义 |
|---|---|---|
| type | eq_ref | 主键或唯一索引查找 |
| key | PRIMARY | 使用的索引 |
| rows | 1 | 预估检查行数 |
| Extra | Using index | 索引覆盖 |
5. 复杂业务场景综合题
5.1 电商订单分析
模拟电商数据库schema:
sql复制CREATE TABLE users (
user_id INT PRIMARY KEY,
user_name VARCHAR(50)
);
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100),
price DECIMAL(10,2)
);
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT,
order_date DATE,
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
CREATE TABLE order_items (
item_id INT PRIMARY KEY,
order_id INT,
product_id INT,
quantity INT,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
题目6:查询2023年消费金额TOP10的用户
sql复制SELECT
u.user_id,
u.user_name,
SUM(oi.quantity * p.price) as total_spent
FROM users u
JOIN orders o ON u.user_id = o.user_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.order_date BETWEEN '2023-01-01' AND '2023-12-31'
GROUP BY u.user_id, u.user_name
ORDER BY total_spent DESC
LIMIT 10;
5.2 递归查询应用
组织架构表设计:
sql复制CREATE TABLE employees_hier (
emp_id INT PRIMARY KEY,
emp_name VARCHAR(50),
manager_id INT,
FOREIGN KEY (manager_id) REFERENCES employees_hier(emp_id)
);
题目7:查询指定员工的所有下属(多级)
sql复制WITH RECURSIVE emp_tree AS (
-- 基础查询:获取直接下属
SELECT emp_id, emp_name, manager_id, 1 as level
FROM employees_hier
WHERE manager_id = 1001
UNION ALL
-- 递归查询:获取下级的下级
SELECT e.emp_id, e.emp_name, e.manager_id, et.level + 1
FROM employees_hier e
JOIN emp_tree et ON e.manager_id = et.emp_id
)
SELECT * FROM emp_tree ORDER BY level;
6. 常见错误与调试技巧
6.1 语法错误排查
典型错误示例:
sql复制-- 错误:GROUP BY与SELECT列不匹配
SELECT dept_id, emp_name, AVG(salary)
FROM employees
GROUP BY dept_id;
-- 正确写法
SELECT dept_id, AVG(salary)
FROM employees
GROUP BY dept_id;
6.2 性能问题诊断
慢查询优化步骤:
- 使用EXPLAIN分析执行计划
- 检查是否使用正确索引
- 避免全表扫描(type=ALL)
- 减少临时表使用(Extra=Using temporary)
- 控制返回数据量(LIMIT分页)
6.3 事务处理要点
sql复制START TRANSACTION;
-- 操作1
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1;
-- 操作2
UPDATE accounts SET balance = balance + 100 WHERE user_id = 2;
-- 根据业务逻辑决定提交或回滚
COMMIT;
-- 或 ROLLBACK;
事务隔离级别对比:
| 级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
|---|---|---|---|---|
| READ UNCOMMITTED | ✓ | ✓ | ✓ | 最高 |
| READ COMMITTED | × | ✓ | ✓ | 高 |
| REPEATABLE READ | × | × | ✓ | 中 |
| SERIALIZABLE | × | × | × | 低 |
在实际项目中,我发现很多SQL问题都源于对业务场景理解不足。建议在编写复杂SQL前,先用自然语言描述清楚要实现的业务逻辑,然后再转化为SQL语句。对于特别复杂的查询,可以分步骤构建,先用子查询或CTE分解问题,再组合成完整方案。