必须用窗口函数 ROW_NUMBER() 按省份分区排序,才能实现各省独立取前10;错误做法是 GROUP BY 后直接 ORDER BY + LIMIT,结果为全国 Top10。
ROW_NUMBER() 排名,避免 GROUP BY 后丢失城市粒度直接对省份 + 城市分组求和再排序,容易误用 ORDER BY 仅作用于最终结果集——这没法保证“每个省内独立取前10”。必须用窗口函数在分组聚合后、按省份分区排序。
常见错误是写成 SELECT province, city, SUM(sales) FROM t GROUP BY province, city ORDER BY SUM(sales) DESC LIMIT 10,这返回的是全国销售额 Top10,不是各省 Top10。
正确做法分两步:先聚合(GROUP BY province, city),再用 ROW_NUMBER() OVER (PARTITION BY province ORDER BY SUM(sales) DESC) 标记名次:
SELECT province, city, sales_sumFROM ( SELECT province, city, SUM(sales) AS sales_sum, ROW_NUMBER() OVER (PARTITION BY province ORDER BY SUM(sales) DESC) AS rn FROM orders GROUP BY province, city) rankedWHERE rn <= 10;
ROW_NUMBER() 和 RANK() 的并列处理差异如果某省第10名有多个城市销售额相同,ROW_NUMBER() 会强制给唯一序号(比如 9、10、11),导致实际返回 11 行;而 RANK() 会并列(比如 9、10、10、12),此时 WHERE rn 可能漏掉并列的第10名。
ROW_NUMBER()
RANK(),但 WHERE 条件得改成 r (别漏改别名)
SUM(sales) 结果失真真实订单表常含 sales 为 NULL 或负值(退货)、province/city 为空或“未知”、“其他”等非标准值。这些不剔除,会导致:
SUM() 忽略 NULL,但若整组都是 NULL,该城市不出现 → 看似“没数据”,实为脏数据掩盖province 字段里,和真实省份同名分区 → 分区错乱建议在子查询中加清洗条件:
WHERE sales > 0 AND province IS NOT NULL AND city NOT IN ('未知', '其他', '') AND province IN ('广东', '江苏', '浙江', ...)
当 orders 表超千万行,上述查询可能慢在两处:按 province/city 分组扫描、以及窗口函数排序。单列索引效果有限。
推荐创建覆盖索引:
CREATE INDEX idx_province_city_sales ON orders (province, city, sales);
这个顺序很重要:province 在前支持分区裁剪,city 第二支持组内去重,sales 最后让 SUM() 和 ORDER BY 尽量走索引有序扫描,避免额外排序。
如果业务中经常查“某省 Top N”,还可考虑物化省份汇总表,但实时性要求高时,索引 + 窗口函数仍是更轻量的选择。