应优先使用 row_number() 窗口函数实现编号,因其语义清晰、跨数据库兼容(sql server 和 mysql 8.0+ 均支持),而用户变量(如 @i := @i + 1)在 mysql 5.7 及以前版本中行为不可靠,sql server 更不支持;row_number() 必须配合 over() 子句,可搭配 order by 或 partition by 实现全局或分组编号,且结果仅在当前查询有效,需用 cte 或子查询固化。

直接用 ROW_NUMBER(),别碰用户变量或临时表——它最稳、语义最清晰、SQL Server 和 MySQL 8.0+ 都原生支持。
为什么不用 @i := @i + 1 这类变量写法
MySQL 5.7 及更早版本中,@i := @i + 1 看似能编号,但实际行为不可靠:执行计划变动、优化器重排、并行扫描都可能导致序号跳变或重复。官方文档明确指出“用户变量在 SELECT 中的赋值顺序不保证”,尤其加了 ORDER BY 或 LIMIT 后更容易出错。SQL Server 根本不支持这种语法,强行移植会报错 Incorrect syntax near '@i'。
ROW_NUMBER() 必须配 OVER(),否则语法错误
ROW_NUMBER() 是窗口函数,不能单独使用。漏写 OVER() 会触发类似 Incorrect syntax near ')' 的解析失败。
- 正确写法:
SELECT ROW_NUMBER() OVER (ORDER BY id) AS rn, name FROM users; - 如果不想排序,又必须编号,就用
OVER (ORDER BY (SELECT NULL))—— 这是 SQL Server 兼容写法;MySQL 8.0+ 支持OVER ()(空括号),但为跨平台稳妥起见,仍建议显式写(SELECT NULL) - 注意:
ORDER BY里不能引用别名,比如OVER (ORDER BY rn)会报错,只能用原始列名或表达式
需要分组内编号?改用 PARTITION BY
比如按部门给员工编号,每个部门从 1 开始:用 PARTITION BY dept_id 切分窗口,再在各组内排序编号。
SELECT dept_id, name, ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rank_in_dept FROM employees;
- 分组字段类型要一致,比如
dept_id是INT,就别混着用字符串型 ID,否则分区失效 -
PARTITION BY后不能跟WHERE条件,过滤必须放在外层或 CTE 中,否则可能漏掉分组首行 - 若某组数据为空(如某部门没员工),该组不会产生任何行,
ROW_NUMBER()不会补 0 或占位
真正容易被忽略的是:序号只在当前查询生命周期内存在,它不是存储值,也不影响源表结构。如果后续要基于这个序号做 JOIN 或 UPDATE,得用 CTE 或子查询固化结果,否则重复执行时序号可能因数据变动而偏移。










