row_number()是最可靠方案,需配合partition by和order by分组排序并嵌套子查询筛选rn=1;mysql 5.7及更早不支持,须用相关子查询或join替代。

用 ROW_NUMBER() 窗口函数最可靠
直接在分组内编号,再筛出序号为 1 的行,是目前主流数据库(PostgreSQL、SQL Server、Oracle、MySQL 8.0+)中最可控、语义最清晰的方式。它不依赖字段唯一性,也不要求排序字段严格可比。
常见错误是写成 GROUP BY 配合 SELECT *,这在标准 SQL 中非法,在 MySQL 5.7 以下开启 ONLY_FULL_GROUP_BY 时会报错:Expression #1 of SELECT list is not in GROUP BY clause。
- 必须显式指定排序依据,否则
ROW_NUMBER()的“第一行”无定义——同一组内若多行排序值相同,结果可能每次执行都不一样 - 排序字段建议组合使用,例如
ORDER BY status DESC, created_at ASC,避免仅靠单个可能重复的字段(如status) - 别名不能在
WHERE中直接引用,需套一层子查询或 CTE:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY score DESC) AS rn FROM products ) t WHERE rn = 1;
MySQL 5.7 及更早版本只能靠相关子查询或 JOIN
这些版本不支持窗口函数,也不能依赖 GROUP BY 返回非聚合字段。强行用 GROUP BY + SELECT * 虽然能跑,但返回的是每组中某一行(MySQL 自选),不可预测、不可迁移。
安全做法是用相关子查询找每组最大/最小值,再关联回原表:
- 假设要取每组
category下score最高的那行,且(category, score)组合不重复: - 写法:先查每组最大
score,再用JOIN或IN匹配完整记录 - 注意:如果存在多个相同最高
score的行,该方法会返回全部——和ROW_NUMBER()行为不同,后者只取一行
SELECT p1.* FROM products p1 WHERE p1.score = ( SELECT MAX(p2.score) FROM products p2 WHERE p2.category = p1.category );
慎用 DISTINCT ON(PostgreSQL 专属)
DISTINCT ON 看似简洁,但它不是标准 SQL,仅 PostgreSQL 支持,迁移到其他数据库会直接报错:ERROR: syntax error at or near "ON"。
它的行为也容易误解:不是“每组取排序后第一行”,而是“按 DISTINCT ON 列分组,取该组中 ORDER BY 排序后的首行”。如果 ORDER BY 没覆盖 DISTINCT ON 字段,结果不稳定。
- 必须配合
ORDER BY,且DISTINCT ON的字段必须是ORDER BY的前缀 - 例如
DISTINCT ON (category) ORDER BY category, score DESC合法;DISTINCT ON (category) ORDER BY score DESC报错 - 无法控制“并列第一”时取哪条——它取物理存储顺序靠前的那条,不可控
SELECT DISTINCT ON (category) * FROM products ORDER BY category, score DESC;
GROUP BY + 聚合函数只能取部分字段
如果只需要分组的某个标量值(比如每组最高分、最早时间),用 MAX()、MIN()、STRING_AGG() 等聚合函数最轻量。但它拿不到整行数据,尤其当需要同时返回 name、id、score 等多个非聚合字段时,这条路走不通。
- 试图用
ANY_VALUE(id)(MySQL)或first_value(id)(带窗口)绕过限制,本质仍是不确定行为,不推荐用于关键业务 - 聚合函数本身不解决“第一行”问题,只是回避了它——如果你真需要那一整行的上下文,就得回到窗口函数或子查询
- 性能上,纯聚合通常比窗口函数快,但前提是你的需求确实只要一个值
SELECT category, MAX(score) AS max_score FROM products GROUP BY category;
真正麻烦的地方不在语法,而在“第一行”的定义本身:是按时间最早?分数最高?还是 ID 最小?这个业务含义一旦模糊,所有技术方案都会产出意外结果。别急着写 SQL,先和产品确认清楚排序优先级和并列情况怎么处理。











