1. 项目概述作为一名长期奋战在数据领域的老兵我最近系统性地刷完了力扣LeetCode高频SQL 50题的第三阶段题目。这个系列题目覆盖了SQL查询的各类核心场景从基础的增删改查到复杂的多表关联、窗口函数应用再到各种业务场景下的数据分析需求可以说是SQL技能的试金石。在刷题过程中我发现很多题目看似简单但要在保证性能的前提下写出优雅的解决方案并不容易。特别是当数据量达到百万级时一个不恰当的JOIN操作或缺失的索引就可能导致查询时间从毫秒级飙升到分钟级。本文将分享我在解决这组题目时总结的实战经验包括高频考点解析、性能优化技巧和常见误区规避。2. 高频考点深度解析2.1 窗口函数的进阶应用窗口函数是SQL中功能强大但容易被忽视的特性。在第三阶段的题目中以下几类窗口函数应用尤为高频排名问题RANK()、DENSE_RANK()和ROW_NUMBER()的区别常被考察-- 三种排名函数的区别示例 SELECT student_id, score, RANK() OVER (ORDER BY score DESC) AS rank_score, DENSE_RANK() OVER (ORDER BY score DESC) AS dense_rank_score, ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num FROM exam_results;RANK()相同值排名相同但会跳过后续名次如1,1,3DENSE_RANK()相同值排名相同不跳名次如1,1,2ROW_NUMBER()始终生成唯一序号如1,2,3移动平均与累计求和-- 7日移动平均计算 SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg FROM daily_sales;提示窗口函数中的PARTITION BY子句相当于分组但不聚合这在处理分组内排序问题时特别有用。2.2 复杂JOIN操作的优化策略多表关联是SQL题目的核心难点之一。在解决实际问题时我发现以下几种JOIN策略特别值得关注小表驱动大表原则始终让数据量小的表作为驱动表-- 反例大表在前 SELECT * FROM large_table l JOIN small_table s ON l.id s.id; -- 正例小表驱动 SELECT * FROM small_table s JOIN large_table l ON s.id l.id;索引利用的黄金法则JOIN条件必须建立索引WHERE条件中的高频过滤字段需要索引多列索引要注意最左前缀原则临时表与CTE的合理使用-- 使用CTE简化复杂查询 WITH high_value_customers AS ( SELECT customer_id FROM orders GROUP BY customer_id HAVING SUM(amount) 10000 ) SELECT c.* FROM customers c JOIN high_value_customers h ON c.id h.customer_id;3. 典型题目实战解析3.1 连续登录问题题目#1369这是非常经典的业务场景题要求找出连续登录N天的用户。我的解决方案如下WITH login_dates AS ( SELECT user_id, login_date, DATE_SUB(login_date, INTERVAL ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY login_date) DAY) AS date_group FROM logins GROUP BY user_id, login_date -- 去重 ) SELECT user_id, MIN(login_date) AS start_date, MAX(login_date) AS end_date, COUNT(*) AS consecutive_days FROM login_dates GROUP BY user_id, date_group HAVING COUNT(*) 5; -- 至少连续5天关键技巧使用ROW_NUMBER()和日期运算创建分组标识先对(user_id, login_date)去重避免重复记录干扰最后按分组标识聚合计算连续天数3.2 部门工资前三高的员工题目#185这道题考察窗口函数和子查询的综合应用-- 方案1使用DENSE_RANK SELECT d.name AS Department, e.name AS Employee, e.salary AS Salary FROM ( SELECT *, DENSE_RANK() OVER (PARTITION BY departmentId ORDER BY salary DESC) AS rnk FROM Employee ) e JOIN Department d ON e.departmentId d.id WHERE e.rnk 3; -- 方案2使用相关子查询某些数据库性能更好 SELECT d.name AS Department, e1.name AS Employee, e1.salary AS Salary FROM Employee e1 JOIN Department d ON e1.departmentId d.id WHERE ( SELECT COUNT(DISTINCT e2.salary) FROM Employee e2 WHERE e2.departmentId e1.departmentId AND e2.salary e1.salary ) 3;性能对比方案1在大多数现代数据库上表现良好代码更简洁方案2在MySQL 5.7等旧版本上可能更快但可读性较差4. 性能优化实战技巧4.1 EXPLAIN命令深度使用理解执行计划是SQL优化的基础。以下是我总结的EXPLAIN关键点速查表列名关键含义优化方向type访问类型性能关键指标争取达到const/ref/range级别possible_keys可能使用的索引确保包含实际使用的索引key实际使用的索引检查是否使用了最优索引rows预估检查的行数数值越小越好Extra额外信息避免出现Using filesort等4.2 索引优化实战案例案例慢查询优化-- 原始查询耗时2.3秒 SELECT * FROM orders WHERE user_id 100 AND status completed ORDER BY create_time DESC LIMIT 10; -- 优化方案 -- 1. 创建复合索引 ALTER TABLE orders ADD INDEX idx_user_status_time (user_id, status, create_time); -- 2. 改写查询确保索引覆盖 SELECT id, user_id, amount, create_time -- 只查询需要的列 FROM orders WHERE user_id 100 AND status completed ORDER BY create_time DESC LIMIT 10;优化后查询时间降至23毫秒提升约100倍。关键点复合索引的顺序遵循等值查询列在前范围查询/排序列在后原则避免SELECT *减少数据传输量确保ORDER BY列在索引中且方向一致5. 常见陷阱与避坑指南5.1 NULL值处理陷阱NULL在SQL中是个特殊存在常见陷阱包括比较运算任何与NULL的比较都返回NULL而非TRUE/FALSE-- 错误做法不会返回NULL记录 SELECT * FROM table WHERE column NULL; -- 正确做法 SELECT * FROM table WHERE column IS NULL;聚合函数COUNT(*)与COUNT(column)对NULL的处理不同-- 统计总行数包含NULL SELECT COUNT(*) FROM table; -- 统计某列非NULL值数量 SELECT COUNT(column) FROM table;5.2 隐式类型转换问题数据类型不匹配会导致索引失效-- 假设user_id是字符串类型但存储数字 SELECT * FROM users WHERE user_id 100; -- 索引可能失效 -- 正确做法确保类型匹配 SELECT * FROM users WHERE user_id 100;诊断方法检查EXPLAIN输出中的key列是否使用了预期索引查看数据库慢查询日志中的警告信息6. 刷题方法论与学习路径6.1 高效刷题四步法先思考再编码花5-10分钟分析题目明确输入输出和边界条件多种解法尝试至少用两种不同方法实现比较性能差异极端测试用例设计包含NULL、空集、大数据量的测试案例复盘总结记录每种解法的时空复杂度分析最优方案6.2 推荐学习资源进阶书籍《SQL进阶教程》- 着重讲解窗口函数等高级特性《高性能MySQL》- 深入理解索引和查询优化实战平台LeetCode SQL题库按频度排序HackerRank SQL专项练习公司内部真实业务数据最佳学习材料社区资源Stack Overflow的sql标签高频问题数据库官方文档特别是函数和优化器部分在实际工作中我发现SQL技能的提升是一个持续的过程。每解决一个复杂查询问题都会对数据库的工作原理有更深的理解。建议定期回顾已解决的题目尝试用新学到的优化技巧重构旧代码这种迭代学习的效果往往出乎意料的好。
阅读完成 · 觉得有帮助?