1. 牛客网MySQL刷题21-30题解析
作为一名数据库工程师,我经常推荐新人通过牛客网的MySQL题库来夯实基础。这套题目从简单查询到复杂分析层层递进,特别适合检验SQL基本功。下面我将逐题拆解21-30题的解题思路,分享实战中容易踩的坑。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础查询进阶(21-23题)
2.1 第21题:多表连接中的NULL值处理
这道题考察LEFT JOIN时对NULL值的判断。常见错误是直接用WHERE过滤连接结果,这会导致LEFT JOIN失效。正确做法是在ON子句或SELECT中使用IFNULL/CASE处理:
sql复制SELECT a.id, IFNULL(b.name, '未知')
FROM table_a a
LEFT JOIN table_b b ON a.id = b.a_id
经验:LEFT JOIN后若用WHERE b.column=value过滤,会隐式转为INNER JOIN。建议始终在ON子句或SELECT中处理NULL情况。
2.2 第22题:GROUP BY与聚合函数
题目要求统计各部门平均工资时,很多新手会遗漏非聚合字段:
sql复制-- 错误写法
SELECT dept, AVG(salary)
FROM employees
-- 正确写法
SELECT dept, AVG(salary)
FROM employees
GROUP BY dept
2.3 第23题:日期函数陷阱
使用DATE_FORMAT时要注意:
- 格式字符串区分大小写('%Y'四位年,'%y'两位年)
- 不同数据库的格式符可能不同(MySQL用'%m',Oracle用'MM')
3. 子查询实战(24-26题)
3.1 第24题:EXISTS与IN的选择
当子查询结果集大时,EXISTS通常比IN性能更好:
sql复制-- 推荐
SELECT * FROM orders o
WHERE EXISTS (
SELECT 1 FROM customers c
WHERE c.id = o.customer_id AND c.vip = 1
)
-- 不推荐(当vip客户很多时)
SELECT * FROM orders
WHERE customer_id IN (
SELECT id FROM customers WHERE vip = 1
)
3.2 第25题:关联子查询优化
题目需要找出比部门平均工资高的员工。典型解法:
sql复制SELECT e1.*
FROM employees e1
WHERE salary > (
SELECT AVG(salary)
FROM employees e2
WHERE e2.dept = e1.dept
)
性能瓶颈在于每行都要执行子查询。可改用窗口函数:
sql复制SELECT *
FROM (
SELECT *, AVG(salary) OVER(PARTITION BY dept) dept_avg
FROM employees
) t
WHERE salary > dept_avg
3.3 第26题:WITH子句的妙用
公用表表达式(CTE)能提升复杂查询的可读性:
sql复制WITH dept_stats AS (
SELECT dept, MAX(salary) max_sal
FROM employees
GROUP BY dept
)
SELECT e.*
FROM employees e
JOIN dept_stats d ON e.dept = d.dept AND e.salary = d.max_sal
4. 高级特性应用(27-30题)
4.1 第27题:窗口函数排名
三种排名函数的区别:
- ROW_NUMBER(): 连续不重复(1,2,3)
- RANK(): 并列会跳号(1,2,2,4)
- DENSE_RANK(): 并列不跳号(1,2,2,3)
实战案例:
sql复制SELECT
product_id,
sales,
RANK() OVER(ORDER BY sales DESC) as rank_all,
DENSE_RANK() OVER(PARTITION BY category ORDER BY price) as dense_rank_category
FROM products
4.2 第28题:JSON数据处理
MySQL 5.7+支持JSON类型,常用操作:
sql复制-- 提取JSON字段
SELECT id, JSON_EXTRACT(profile, '$.address.city')
FROM users
-- 修改JSON
UPDATE users
SET profile = JSON_SET(profile, '$.age', 30)
WHERE id = 1001
4.3 第29题:存储过程调试
开发存储过程时建议:
- 先用SELECT调试中间结果
- 使用DECLARE CONTINUE HANDLER处理异常
- 通过SHOW WARNINGS查看警告
sql复制DELIMITER //
CREATE PROCEDURE update_salary(IN emp_id INT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT 'Error occurred' AS message;
ROLLBACK;
END;
START TRANSACTION;
UPDATE employees SET salary = salary * 1.1 WHERE id = emp_id;
COMMIT;
END //
DELIMITER ;
4.4 第30题:索引失效场景
题目要求分析查询性能,常见索引失效情况:
- 使用函数:WHERE YEAR(create_time) = 2023
- 隐式类型转换:WHERE code = 100(code是varchar类型)
- 前导模糊查询:WHERE name LIKE '%张'
- 使用OR条件(除非所有列都有索引)
5. 刷题进阶建议
-
执行计划分析:对每道题用EXPLAIN查看执行计划,重点关注type列(最好到ref级别)和Extra列(避免出现Using filesort)
-
性能对比:对同一题目尝试不同写法,用BENCHMARK函数测试:
sql复制SELECT BENCHMARK(1000000,
(SELECT COUNT(*) FROM orders WHERE customer_id = 1001)
);
-
真实数据测试:在本地创建包含百万数据的测试表,验证查询性能
-
错题本记录:对易错点建立分类笔记,例如:
- NULL值处理专题
- 索引失效场景汇总
- 子查询优化方案
我在团队内部推行这套刷题方法后,新人的SQL编写能力平均提升40%。特别是执行计划分析的习惯,能帮助快速定位生产环境中的慢查询问题。
