分组排名通过窗口函数实现,核心是使用ROW_NUMBER()、RANK()和DENSE_RANK()结合PARTITION BY与ORDER BY,在每组内进行排序。ROW_NUMBER()为每行分配唯一序号,即使并列也强制区分;RANK()在并列时给予相同排名,但后续排名跳跃;DENSE_RANK()则在并列时相同排名且后续不跳号。实际应用中可根据业务需求选择函数,并通过添加额外排序字段确保结果确定性,广泛用于电商Top N商品、金融最大交易、用户行为路径分析等场景。

SQL分组查询实现每组排名统计,核心在于运用窗口函数(Window Functions)。这是一种极其强大且灵活的工具,它允许你在不改变原有分组聚合结果的前提下,对每个分组内的数据进行独立的排序和编号,从而轻松地得到你想要的组内排名。
要实现每组排名统计,我们通常会用到SQL的窗口函数,特别是
ROW_NUMBER()
RANK()
DENSE_RANK()
OVER()
PARTITION BY
ORDER BY
想象一下,我们有一个
students_scores
CREATE TABLE students_scores (
student_id INT,
student_name VARCHAR(50),
course_id INT,
course_name VARCHAR(50),
score INT
);
INSERT INTO students_scores (student_id, student_name, course_id, course_name, score) VALUES
(1, '张三', 101, '数学', 95),
(2, '李四', 101, '数学', 90),
(3, '王五', 101, '数学', 95),
(4, '赵六', 101, '数学', 88),
(5, '钱七', 102, '语文', 80),
(6, '孙八', 102, '语文', 85),
(7, '周九', 102, '语文', 85),
(8, '吴十', 102, '语文', 78),
(9, '郑一', 103, '英语', 92),
(10, '王二', 103, '英语', 92),
(11, '李三', 103, '英语', 89);现在,我们想知道每个课程里学生的成绩排名。这就是分组排名的典型场景。
SELECT
student_name,
course_name,
score,
ROW_NUMBER() OVER (PARTITION BY course_name ORDER BY score DESC) AS row_num_rank,
RANK() OVER (PARTITION BY course_name ORDER BY score DESC) AS rank_rank,
DENSE_RANK() OVER (PARTITION BY course_name ORDER BY score DESC) AS dense_rank_rank
FROM
students_scores
ORDER BY
course_name, score DESC;这段SQL会按照
course_name
score
PARTITION BY course_name
ORDER BY score DESC
理解这三个窗口函数的细微差别,是掌握分组排名的关键。虽然它们都用于生成排名,但在处理并列(ties)情况时,行为却大相径庭。
ROW_NUMBER()
ORDER BY
ROW_NUMBER()
RANK()
RANK()
DENSE_RANK()
RANK()
DENSE_RANK()
RANK()
DENSE_RANK()
用我们上面的学生成绩数据来直观感受一下:
| student_name | course_name | score | row_num_rank | rank_rank | dense_rank_rank |
|---|---|---|---|---|---|
| 张三 | 数学 | 95 | 1 | 1 | 1 |
| 王五 | 数学 | 95 | 2 | 1 | 1 |
| 李四 | 数学 | 90 | 3 | 3 | 2 |
| 赵六 | 数学 | 88 | 4 | 4 | 3 |
| 孙八 | 语文 | 85 | 1 | 1 | 1 |
| 周九 | 语文 | 85 | 2 | 1 | 1 |
| 钱七 | 语文 | 80 | 3 | 3 | 2 |
| 吴十 | 语文 | 78 | 4 | 4 | 3 |
| 郑一 | 英语 | 92 | 1 | 1 | 1 |
| 王二 | 英语 | 92 | 2 | 1 | 1 |
| 李三 | 英语 | 89 | 3 | 3 | 2 |
从表中可以清晰地看到,在“数学”和“英语”课程中,张三/王五(数学95分)和郑一/王二(英语92分)都是并列的,三种函数的行为差异一目了然。选择哪个函数,完全取决于你的业务逻辑对“并列”的定义和处理需求。
处理分组排名中的并列,实际上是根据业务需求选择合适的窗口函数,并在必要时加入额外的排序条件来“打破”并列。这不仅仅是技术实现问题,更是对业务逻辑的深入理解。
当我们遇到并列数据时,首先要明确业务上希望如何对待这些并列项:
DENSE_RANK()
RANK()
ROW_NUMBER()
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY score DESC)
ORDER BY
例如,如果“数学”课中有两个学生都考了95分,我们希望分数高的排前面,分数相同的情况下,
student_id
SELECT
student_name,
course_name,
score,
ROW_NUMBER() OVER (PARTITION BY course_name ORDER BY score DESC, student_id ASC) AS unique_rank_with_tie_breaker
FROM
students_scores
ORDER BY
course_name, score DESC, student_id ASC;这里,
student_id ASC
score
student_id
ROW_NUMBER()
选择哪种处理方式,取决于具体的业务规则。比如,在生成销售排行榜时,并列第一的销售额,可能更倾向于用
RANK()
ROW_NUMBER()
ORDER BY
分组排名远不止于学生成绩排名,它在各种业务场景中都扮演着关键角色,帮助我们从海量数据中提炼出有价值的洞察。它的应用场景非常广泛,几乎涵盖了所有需要“组内Top N”或“组内排序”的需求。
电商平台:找出每个商品类别中最畅销的Top 5商品。 这对于库存管理、商品推荐和市场营销策略至关重要。例如,
SELECT * FROM (SELECT product_name, category, sales, ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) AS rn FROM products) AS ranked_products WHERE rn <= 5;
金融风控:识别每个客户在过去一段时间内交易金额最大的Top 3交易。 这有助于分析客户行为模式,发现异常交易,或者评估客户价值。
网站分析:分析每个用户会话(session)中,用户访问的第一个页面和最后一个页面。 通过
PARTITION BY session_id ORDER BY timestamp ASC
社交媒体:在每个话题标签下,找出最受欢迎的Top 10帖子。 这能帮助平台推荐热门内容,提升用户活跃度。
游戏行业:统计每个服务器中,玩家的等级排名。 或者找出每个公会中贡献值最高的成员,用于奖励和激励。
供应链管理:在每个仓库中,找出库存周转率最低的Top N商品。 这有助于优化库存结构,避免积压。
人力资源:统计每个部门员工的绩效排名。 辅助绩效评估和晋升决策。
这些例子都体现了分组排名的核心价值:将复杂的全局排序问题,分解为更易于管理和分析的局部排序问题。通过这种方式,我们能够更精细地理解数据,并根据特定分组的上下文做出决策,而不是泛泛地看待所有数据。可以说,只要你的数据存在“组”的概念,并且你需要在这个组内进行某种形式的排序或筛选,分组排名就是你不可或缺的工具。
以上就是SQL 分组查询如何实现每组排名统计?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号