ROW_NUMBER 分区内连续编号
ROW_NUMBER() 按 ORDER BY 给分区内每一行编号,从 1 开始连续递增,值相同也不会重复。七个引擎写法相同,常用于去重和分组取前 N 条。差异在能否用 QUALIFY 直接过滤,以及 Flink 流模式必须按固定写法使用。
各引擎写法
| 引擎 | 支持 | 写法 | 说明 | 依据 |
|---|---|---|---|---|
| MaxCompute | 支持 | ROW_NUMBER() OVER ([partition_clause] [orderby_clause]) | 返回 BIGINT。窗口定义里不允许写 frame 子句。支持用 QUALIFY 直接过滤窗口函数结果。 | 官方文档 |
| Hologres | 支持 | ROW_NUMBER() OVER (...) | Hologres V1.1 起由 HQE 执行引擎支持,返回 BIGINT。 | 官方文档 |
| StarRocks | 支持 | ROW_NUMBER() OVER (partition_by_clause order_by_clause) | 编号不重复、不跳号。2.5 起支持 QUALIFY,QUALIFY 后只能跟 ROW_NUMBER、RANK 和 DENSE_RANK。 | 官方文档 |
| Doris | 支持 | ROW_NUMBER() OVER (...) | 返回 BIGINT,从 1 开始连续递增,分区内唯一。 | 官方文档已实测 4.1.5 |
| Hive | 支持 | ROW_NUMBER() OVER (...) | 属于 Hive 的窗口和分析函数。过滤编号需要套一层子查询。 | 官方文档已实测 4.2.1 |
| Spark SQL | 支持 | row_number() OVER (...) | Spark 2.0.0 起提供,按窗口分区内的排序从 1 开始编号。 | 官方文档 |
| Flink SQL | 支持 | ROW_NUMBER() OVER ([PARTITION BY ...] ORDER BY ...) | 主要用于 Top-N 和去重查询,必须严格按文档写法,否则优化器无法识别。Top-N 结果会不断更新,写入外部存储时,结果表要有与查询相同的唯一键。 | 官方文档已实测 2.3.0 |
迁移注意事项
- ORDER BY 的列有相同值时,这些行谁排第一不确定,两次运行可能取到不同的行。去重时要在 ORDER BY 末尾加一个唯一列,例如主键。
- QUALIFY 可以省掉一层子查询。本站查到 MaxCompute、StarRocks 2.5 起和 Flink 的文档支持;其他引擎没有查到相关文档,迁移时统一写成子查询最稳妥。
- Flink 流模式下,Top-N 要写成“子查询算 ROW_NUMBER,外层 WHERE rownum <= N”的固定形式。去重时 ORDER BY 必须是处理时间或事件时间属性。写法不符时优化器无法识别。
- Flink Top-N 输出的是会更新的结果,排名变化时会向下游发送撤回和更新记录。下游表要支持按唯一键更新。
- MaxCompute 的 ROW_NUMBER 不允许写 frame 子句,例如 ROWS BETWEEN ... 会报错。
示例
每个用户只保留最新一笔订单
SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, row_number() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, row_number() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) t WHERE rn = 1;SELECT user_id, order_id, order_time FROM (SELECT user_id, order_id, order_time, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY order_time DESC, order_id DESC) AS rn FROM orders) WHERE rn = 1;