ROW_NUMBER()必须配合OVER()使用,否则报错;OVER中至少需ORDER BY,不可为空或仅PARTITION BY;编号在WHERE和LIMIT前生成,需用子查询或CTE实现过滤后重编号。
单独写 ROW_NUMBER() 会报错,比如 SELECT id, ROW_NUMBER() 直接运行提示 “窗口函数必须有 OVER 子句”。它不是普通标量函数,而是窗口函数,依赖排序和分组逻辑。
常见错误是漏掉 ORDER BY —— OVER() 里至少得有一个 ORDER BY,否则语法不通过。如果只是想按自然顺序编号(比如插入顺序),可以用主键或时间字段,例如 OVER (ORDER BY id) 或 OVER (ORDER BY created_at)。
OVER (ORDER BY col1, col2):按多列联合排序后编号,等价于先按 col1 升序,相同时再按 col2 升序OVER (PARTITION BY dept_id ORDER BY salary DESC):先按部门分组,每组内按薪资降序编号OVER ()(空括号),也不能只写 OVER (PARTITION BY x) 而不带 ORDER BY
JOIN 多张表后,ROW_NUMBER() 的 ORDER BY 只能引用 SELECT 列表中出现的字段,或者原始表中明确可识别的列(前提是没歧义)。如果两表都有 id,直接写 ORDER BY id 会报错 “列名不明确”。
实操建议:
SELECT u.id AS user_id, o.order_id, ...,然后在 OVER 中用 ORDER BY user_id, order_id
ORDER BY u.created_at, o.amount,前提是 u 和 o 在查询中已定义ORDER BY 中引用未出现在 SELECT 中的计算字段(某些数据库如 PostgreSQL 允许,但 MySQL 8.0+ 和 SQL Server 要求必须出现在 SELECT 列表)ROW_NUMBER() 是在窗口计算阶段生成的,发生在 WHERE 过滤和 LIMIT 截断之前。这意味着:如果先编号再加 WHERE status = 'active',编号仍是基于全量 JOIN 结果;如果想对过滤后的结果重新编号,得把过滤逻辑放进子查询或 CTE。
例如:
SELECT *, ROW_NUMBER() OVER (ORDER BY user_id) AS rnFROM ( SELECT u.id AS user_id, o.amount FROM users u JOIN orders o ON u.id = o.user_id WHERE o.status = 'paid') t
这样 rn 才是针对已过滤的“已支付订单”重新编号。直接在外面加 WHERE 会导致编号跳跃(比如原编号 1,2,3,4,5,过滤掉第2条后变成 1,3,4,5)。
ROW_NUMBER() 本身不处理 NULL,但 ORDER BY 中遇到 NULL 时,MySQL 默认把 NULL 放最前,PostgreSQL 默认放最后,SQL Server 取决于 SET ANSI_NULLS 设置。这会导致相同 SQL 在不同库中编号顺序不同。
稳妥做法:
ORDER BY COALESCE(updated_at, '1970-01-01')
ORDER BY col IS NULL, col(MySQL/PostgreSQL 支持),让 NULL 排末尾多列关联后编号看似简单,真正踩坑的地方往往不在语法,而在排序语义是否与业务预期一致——特别是涉及 NULL、JOIN 字段歧义、以及过滤时机。