不能。ROW_NUMBER() 不分组,需用 PARTITION BY 切分逻辑组(NULL 归同一组,重复值同组),且必须配合 ORDER BY 保证编号稳定;无可靠排序列时可用子查询加 ROW_NUMBER() OVER (ORDER BY (SELECT 0)) 模拟顺序;去重编号应选 DENSE_RANK() 而非 ROW_NUMBER()。

ROW_NUMBER() 能不能直接按非唯一键分组编号?
不能。ROW_NUMBER() 本身不“分组”,它只按 ORDER BY 排序后挨个编号;所谓“按非唯一键分组编号”,实际是靠 PARTITION BY 配合 ORDER BY 实现的——PARTITION BY 才负责切分逻辑组,哪怕字段值重复、无主键、甚至全 NULL 也能分区。
PARTITION BY 遇到 NULL 或重复值会怎样?
PARTITION BY 把所有 NULL 归为同一组(标准 SQL 行为),重复值自然同组。关键陷阱在于:如果漏写 ORDER BY,结果顺序不确定,同一组内编号每次执行可能不同——这在分页、取 Top N 或导出时会导致数据漂移。
- 必须写
ORDER BY,哪怕只是ORDER BY (SELECT NULL)强制稳定(但不推荐,语义不清) - 更稳妥的是选一个能打破并列的列,比如时间戳、ID,或组合
ORDER BY created_at, id - 若业务允许“同组内任意顺序”,至少用
ORDER BY (SELECT 0)显式声明,避免隐式依赖物理存储顺序
给相同 user_id 打上 1/2/3… 序号,但保留原始行顺序
原始顺序 ≠ 主键顺序,也不等于插入顺序,得靠已有列模拟。常见做法是用自增 ID、时间戳,或添加 ROW_NUMBER() OVER (ORDER BY (SELECT 0)) 先生成稳定序号再参与分区。
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY user_id
ORDER BY id -- 用 id 保证每组内顺序可重现
) AS rn
FROM orders;如果表里真没可靠排序依据,又必须“按查出来顺序编”,可在子查询中先加一层 ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS seq,再在外层 PARTITION BY user_id ORDER BY seq ——但要注意:这个“查出来顺序”仅对单次查询成立,不可跨执行复现。
想跳过重复行只给每组标一个号,该用 ROW_NUMBER 还是 DENSE_RANK?
用 DENSE_RANK()。因为 ROW_NUMBER() 对每行都编号,即使 user_id 和 status 完全一样,也会打出 1/2/3;而 DENSE_RANK() OVER (PARTITION BY user_id ORDER BY status) 会让同 status 的行共享同一个号。
不过注意:DENSE_RANK 和 RANK 不处理“去重”,只是对排序后并列值分配相同序号;真要去重打标签,得先 GROUP BY 或用 DISTINCT 套一层。
最易被忽略的一点:窗口函数的执行时机在 GROUP BY 和 HAVING 之后、ORDER BY 之前。如果误把 ROW_NUMBER() 放在含聚合的查询里又没套子查询,会报错或结果错乱。

















