MySQL 8.0+才支持ROW_NUMBER(),5.7及更早版本需用相关子查询等替代;使用ROW_NUMBER()须通过子查询或CTE编号后过滤rn=1,不可在WHERE中直接调用;FIRST_VALUE()仅返回单字段值,无法获取整行且易致字段错位。

MySQL 8.0+ 才支持 ROW_NUMBER(),低版本必须换思路
ROW_NUMBER() 和 FIRST_VALUE() 是窗口函数,MySQL 5.7 及更早版本完全不识别,直接执行会报错 FUNCTION xxx does not exist 或 You have an error in your SQL syntax。确认版本最简单的方法是运行 SELECT VERSION();。如果结果是 8.0.1+,才能继续用窗口函数;否则得用 GROUP BY 配合子查询、或自连接、或 JSON_AGG + JSON_EXTRACT(MySQL 5.7+)等替代方案。
用 ROW_NUMBER() 获取每组第一条记录的写法要点
核心是先用窗口函数编号,再过滤出序号为 1 的行。不能在同一个 SELECT 中直接加 WHERE ROW_NUMBER() OVER (...) = 1,因为窗口函数不能出现在 WHERE 子句里。
- 必须用子查询或 CTE:把带
ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...)的结果作为临时表,外层再WHERE rn = 1 -
PARTITION BY字段要和你的“分组依据”严格一致,比如按category分组就写PARTITION BY category -
ORDER BY决定哪条算“第一”,例如按时间倒序取最新一条,就写ORDER BY created_at DESC;若时间相同,建议补一个确定性字段如id避免结果不稳定 - 示例:
WITH ranked AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY created_at DESC, id DESC) AS rn FROM products ) SELECT id, category, name, created_at FROM ranked WHERE rn = 1;
FIRST_VALUE() 适合只取某个字段值,不适合整行
FIRST_VALUE() 返回的是窗口内排序后第一个值,它不会减少行数——原表有多少行,结果就有多少行,只是多了一列“每组第一个 xxx”。所以它无法替代“取每组第一条完整记录”的需求。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 如果你只需要某字段(如每组最高价格),
FIRST_VALUE(price) OVER (PARTITION BY category ORDER BY price DESC)是可行的 - 但如果你想拿到对应那条记录的
id、name等其他字段,FIRST_VALUE()无能为力,强行用会导致字段错位(比如 price 对得上,但 name 是另一行的) - 常见误用:
SELECT FIRST_VALUE(id), FIRST_VALUE(name) ... GROUP BY category—— 这语法本身就不合法,GROUP BY 和窗口函数混用需谨慎,且没解决“关联字段一致性”问题
没有窗口函数时,MySQL 5.7 的可靠替代方案
最稳妥的是相关子查询:对每组的每条记录,检查是否存在同组中“排序更优”的另一条记录;若不存在,说明当前这条就是第一。
- 例如取每组最新一条:
SELECT p1.* FROM products p1 WHERE NOT EXISTS ( SELECT 1 FROM products p2 WHERE p2.category = p1.category AND p2.created_at > p1.created_at ); - 性能关键点:务必给
(category, created_at)建联合索引,否则全表扫描极慢 - 注意 NULL 处理:如果
created_at可能为 NULL,> p1.created_at会跳过它们,导致 NULL 行被误选为“第一”,必要时加AND p2.created_at IS NOT NULL - 如果排序字段有重复(如多个同秒创建的记录),该方法可能返回多条,而
ROW_NUMBER()可通过追加id确保唯一性
实际业务中,“分组取第一”往往隐含了“最新/最高/最低”的业务含义,别只盯着语法是否跑通,重点看结果是否稳定可预期——尤其是时间字段精度不足、ID 未单调递增、或存在大量 NULL 时,最容易漏数据或拿错行。

















