1. 理解题目需求
LeetCode 178题"分数排名"是一个典型的SQL排名问题,要求我们编写SQL查询语句,根据分数从高到低对玩家进行排名。排名规则是:相同分数的玩家获得相同的排名,且排名数字连续不跳跃(即DENSE_RANK函数的效果)。
这个题目考察的核心知识点是SQL中的窗口函数应用,特别是RANK()、DENSE_RANK()和ROW_NUMBER()这几个排名函数的区别与使用场景。在实际业务场景中,这类排名操作常见于用户积分排行榜、销售业绩排名等数据分析需求。
2. 数据表结构与示例
首先我们来看题目给出的表结构:
CREATE TABLE Scores ( id INT, score DECIMAL(3,2) );表中包含两个字段:
- id:玩家唯一标识
- score:玩家得分,保留两位小数
示例数据可能如下:
| id | score |
|---|---|
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
3. 排名函数对比分析
解决这个问题前,我们需要清楚理解SQL中三种常见排名函数的区别:
ROW_NUMBER():连续编号,不考虑相同值
- 示例结果:1,2,3,4,5,6
RANK():相同值同排名,后续排名跳跃
- 示例结果:1,2,3,3,5,6(两个3.65并列第3,下一个3.85是第5名)
DENSE_RANK():相同值同排名,后续排名连续
- 示例结果:1,2,3,3,4,5(两个3.65并列第3,下一个3.85是第4名)
题目要求的是DENSE_RANK的效果,即相同分数同排名且后续排名连续不跳跃。
4. 解决方案实现
4.1 基础解法
最直接的解决方案是使用DENSE_RANK()窗口函数:
SELECT score, DENSE_RANK() OVER (ORDER BY score DESC) AS 'rank' FROM Scores ORDER BY score DESC;这个查询会返回:
- 第一列:分数
- 第二列:对应的排名(从高到低)
4.2 不使用DENSE_RANK的替代方案
如果面试中要求不使用窗口函数,我们可以使用自连接的方式实现:
SELECT s1.score, COUNT(DISTINCT s2.score) AS 'rank' FROM Scores s1 LEFT JOIN Scores s2 ON s1.score <= s2.score GROUP BY s1.id, s1.score ORDER BY s1.score DESC;这个方法的原理是:
- 对每个分数s1.score,统计有多少个不同的分数s2.score大于等于它
- 这个数量就是该分数的排名
- 使用DISTINCT确保相同分数只计一次
5. 执行计划与性能分析
对于大数据量的情况,我们需要考虑查询性能:
窗口函数方案:
- 现代数据库对窗口函数优化良好
- 只需要一次表扫描
- 时间复杂度O(n log n)(排序开销)
自连接方案:
- 需要自连接操作,性能较差
- 时间复杂度O(n²)
- 大数据量时不推荐
提示:实际业务中,如果经常需要查询排名,可以考虑预计算并缓存排名结果。
6. 常见问题与解决方案
6.1 如何处理NULL值
如果score字段允许NULL:
- 窗口函数中NULL会被排在最前或最后(取决于ORDER BY指定ASC/DESC)
- 可以使用COALESCE(score, 0)处理NULL值
6.2 相同分数的排名顺序
题目没有明确说明相同分数的显示顺序,实际中可以:
- 按id升序/降序排列
- 添加额外的ORDER BY条件
6.3 分页查询优化
对于排行榜分页查询,可以:
WITH RankedScores AS ( SELECT id, score, DENSE_RANK() OVER (ORDER BY score DESC) AS 'rank' FROM Scores ) SELECT * FROM RankedScores WHERE 'rank' BETWEEN 11 AND 20 ORDER BY 'rank';7. 实际业务场景扩展
这类排名问题在实际业务中有广泛应用:
- 游戏玩家排行榜:显示玩家积分排名
- 销售业绩排名:销售人员的业绩比较
- 学生成绩排名:考试成绩统计分析
- 热门内容排序:文章/视频的热度排名
在更复杂的场景中,可能还需要:
- 按时间范围排名(如月度排名)
- 分组排名(如各部门内部排名)
- 加权分数计算(如考虑多个指标)
8. 不同数据库的语法差异
虽然窗口函数是SQL标准,但不同数据库实现有细微差别:
MySQL:
- 5.7及以下版本不支持窗口函数
- 8.0+版本支持
SQL Server:
- 支持窗口函数
- 语法基本相同
Oracle:
- 支持窗口函数
- 早期版本使用分析函数语法
PostgreSQL:
- 全面支持窗口函数
- 性能优化良好
9. 面试常见考察点
在技术面试中,这类问题通常会考察:
- 对窗口函数的理解程度
- 多种解决方案的掌握(特别是当限制使用某些函数时)
- SQL性能优化的思考
- 对业务场景的抽象能力
建议准备时:
- 熟记RANK/DENSE_RANK/ROW_NUMBER的区别
- 掌握不使用窗口函数的替代方案
- 了解执行计划和性能优化方法
10. 刷题建议与学习资源
针对LeetCode SQL题目的高效刷题建议:
分类练习:
- 先集中练习排名类题目
- 然后练习连接、子查询等类型
理解执行计划:
- 使用EXPLAIN分析查询
- 比较不同写法的性能差异
推荐学习资源:
- 《SQL进阶教程》窗口函数章节
- LeetCode SQL题目讨论区
- 数据库官方文档(如MySQL 8.0 Window Functions)
实战技巧:
- 先写注释梳理思路再写SQL
- 测试边界条件(如NULL、重复值)
- 考虑大数据量下的性能
我在实际刷题中发现,掌握窗口函数后,约60%的中等难度SQL题都能迎刃而解。特别是DENSE_RANK(),在解决排名相关问题时非常高效。建议先从这道题入手,深入理解后再扩展到其他窗口函数应用场景。