不能直接用 GROUP BY 配合 LIMIT,因为 GROUP BY 不保留组内顺序且 LIMIT 作用于最终结果而非每组;应使用 ROW_NUMBER() 窗口函数配合 PARTITION BY 和 ORDER BY 编号后过滤。

为什么不能直接用 GROUP BY 配合 LIMIT
因为 GROUP BY 本身不保留组内行序,LIMIT 也无法作用于每个分组。你写 SELECT * FROM t GROUP BY category ORDER BY score DESC LIMIT 3,结果是整张表取前三,不是每组前三。
用 ROW_NUMBER() 按分组排序后过滤
这是最常用也最可控的方式:先用窗口函数给每组内的记录编号,再在外面筛选编号 ≤ 3 的行。
关键点:
-
ROW_NUMBER()必须配合OVER (PARTITION BY ... ORDER BY ...)使用,缺一不可 -
PARTITION BY决定“分组依据”,ORDER BY决定组内排序逻辑(比如score DESC) - 不能在同一个查询层级里对窗口函数结果做条件过滤(即不能在
WHERE中写ROW_NUMBER() ),必须用子查询或 CTE
SELECT category, name, score
FROM (
SELECT category, name, score,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY score DESC) AS rn
FROM scores
) ranked
WHERE rn <h3>RANK() 和 DENSE_RANK() 的区别在哪</h3><p>当存在并列分数时,三者行为不同:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill10796" title="MySQL8 CRM Schema Design Expert"><img
src="https://img.php.cn/upload/skill/001/246/273/6adaf20dbae7c507.jpg" alt="MySQL8 CRM Schema Design Expert" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill10796" title="MySQL8 CRM Schema Design Expert" class="overflowclass">MySQL8 CRM Schema Design Expert</a>
<p class="overflowclass">面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。</p>
</div>
<a rel="nofollow" href="/xiazai/skill10796" title="MySQL8 CRM Schema Design Expert" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
-
ROW_NUMBER():严格按顺序编号,相同分数也会分出 1/2/3(比如 95, 95, 90 → 1, 2, 3) -
RANK():并列名次相同,但会跳过后续编号(95, 95, 90 → 1, 1, 3) -
DENSE_RANK():并列名次相同,不跳号(95, 95, 90 → 1, 1, 2)
如果你要“取分数前三高的所有选手”,且允许并列,RANK() 更合适;如果明确只要三条记录,哪怕并列也要硬卡数量,就用 ROW_NUMBER()。
性能和索引注意事项
窗口函数本身不走索引,但 PARTITION BY 和 ORDER BY 字段的组合如果有联合索引,能显著加速排序过程。
- 推荐建索引:
ALTER TABLE scores ADD INDEX idx_cat_score (category, score DESC); - MySQL 8.0 对
ORDER BY ... DESC在索引中支持良好,但老版本可能忽略DESC定义 - 如果数据量极大(千万级以上),且只查少数几个 category,可先用
WHERE category IN (...)过滤再开窗,避免全表扫描
别忘了检查执行计划:EXPLAIN FORMAT=TREE 能看到是否用了索引以及是否触发了 filesort。










