rank()需配合over(order by 销售额 desc)使用,处理并列时跳名次,跨年对比须分年计算后join对齐,并注意数据类型、数据库版本及日期字段格式。

用 RANK() 计算单年各省份销售额排名
直接在子查询或 CTE 中对某一年数据用 RANK() 排序是最基础的一步。注意必须用 ORDER BY 销售额 DESC,否则排名是升序(最低值排第1),和业务直觉相反。
常见错误:漏写 OVER 子句,写成 RANK(销售额) —— 这会报错,RANK() 是窗口函数,必须带 OVER。
示例(2023年):
SELECT 省份, 销售额,
RANK() OVER (ORDER BY 销售额 DESC) AS rank_2023
FROM sales
WHERE 年份 = 2023;
-
RANK()会跳过并列后的名次(比如两个第1名,则下一个为第3名),如果要连续编号,改用DENSE_RANK() - 若需按区域分组再排名(如“华东各省内部排名”),加
PARTITION BY 区域 - 确保
销售额字段为数值类型,字符串型数字(如 '12000')会导致字典序排序,结果错乱
跨年度对比需先对齐两年数据
不能直接在原表上对两年数据一起 RANK(),否则排名混合了不同年份,失去“各年独立排名”的意义。必须把两年的排名结果作为两列并到同一行,才能计算波动。
推荐用 FULL JOIN 或 UNION ALL + GROUP BY 实现对齐,但更稳妥的是用两个子查询做 LEFT JOIN,以2023年省份为主表,补全2024年缺失值。
示例(主查2023,左连2024):
SELECT a.省份,
a.rank_2023,
COALESCE(b.rank_2024, 999) AS rank_2024,
a.rank_2023 - COALESCE(b.rank_2024, 999) AS rank_change
FROM (
SELECT 省份, RANK() OVER (ORDER BY 销售额 DESC) AS rank_2023
FROM sales WHERE 年份 = 2023
) a
LEFT JOIN (
SELECT 省份, RANK() OVER (ORDER BY 销售额 DESC) AS rank_2024
FROM sales WHERE 年份 = 2024
) b ON a.省份 = b.省份;
- 用
COALESCE(b.rank_2024, 999)是为了把2024年没销售的省份标为“严重下滑”,避免NULL参与减法得NULL - 若某省2024年新增销售,但2023年无记录,
LEFT JOIN会漏掉它;此时应换FULL JOIN并分别处理两边的NULL - 注意:两年数据量级差异大时(如2024整体翻倍),单纯看排名波动可能掩盖真实增长,建议同步看销售额绝对值变化
计算排名波动时如何处理并列与新增/退出省份
真实业务中,常有省份销售额并列、新设行政区(如直辖市扩容)、或某省全年无销售。这些都会让 RANK() 结果和波动解读变复杂。
关键原则:波动值本身不重要,重要的是波动背后的含义是否一致。例如两个省都从第5升到第3,但一个靠增长20%,一个靠同行衰退——不能等同看待。
- 并列排名下,
RANK()给相同值(如都为3),但下次排名跳到5。计算波动时,若A、B同为第3,次年A第2、B第4,则波动分别是 -1 和 +1,合理 - 新增省份默认排最后(
RANK()在其所在年份内生效),但若直接用“上年无记录 → 今年排第30”,会误判为“跃升30名”。应在结果中标记is_new_province = 1 - 退出省份(如某省全年0销售额)建议单独过滤或标记为
rank = NULL,不要硬填999,否则和“真实垫底”混淆
性能与兼容性提醒:MySQL 8.0+、PostgreSQL、SQL Server 都支持,但旧版本不行
如果你用的是 MySQL 5.7 或更早版本,RANK() 不可用,强行使用会报错 FUNCTION xxx.RANK does not exist。别试图用变量模拟,逻辑易错且无法并行。
替代方案只有两种:升级数据库,或改用应用层排序(导出后用 Python/Pandas 处理)。后者在数据量超10万行时明显变慢,且无法嵌入报表 SQL。
另外注意:SQLite 默认不支持窗口函数(除非编译时开启),而某些云数仓(如 Hive)虽支持 RANK() ,但 OVER 中不支持 PARTITION BY 嵌套表达式,需提前聚合。
最易被忽略的一点:日期字段若存为字符串(如 '2023-01'),用 WHERE 年份 = 2023 会隐式转换失败,导致空结果——务必确认年份字段是整型或用 YEAR(日期列) 提取。










