窗口函数排序稳定性由over子句中order by完全决定,与事务隔离级别无关;真正导致排名波动的是排序列重复且未加唯一决胜列,如order by score desc, id asc。

窗口函数的排序稳定性不依赖事务隔离级别
窗口函数计算时的行序由 OVER 子句中的 ORDER BY 完全决定,和事务是否并发、隔离级别设为 READ COMMITTED 还是 SERIALIZABLE 无关。哪怕在高并发插入/更新场景下,只要 ORDER BY 表达式能唯一确定每行位置,排名结果就稳定可重现。
真正导致排名波动的只有排序列重复且无决胜列
常见错误现象:同一 SQL 多次执行,RANK() OVER (ORDER BY score DESC) 返回的名次顺序不一致,尤其在有多个相同 score 的记录时。
- 根本原因不是并发冲突,而是
score相同时数据库按物理存储顺序或扫描路径排——这在并发写入后可能变化 -
ORDER BY score DESC, id ASC才是解法:加一个非空唯一列(如主键id或rowid)作为决胜列 - 如果
id允许为 NULL,必须先过滤或用COALESCE(id, 0)处理,否则 NULL 参与排序会引入不确定性
UPDATE/INSERT 并发时,窗口函数看到的是快照数据
在支持 MVCC 的数据库(PostgreSQL、Oracle、SQL Server)中,每个查询看到的是事务开始时刻的一致性快照。这意味着:
- 即使其他事务正在修改
salary字段,你的ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC)始终基于该快照计算,不会读到“中间态” - 但如果你的查询没加事务控制,又在应用层反复执行,两次查询可能基于不同快照——这时差异来自数据本身变化,而非窗口函数逻辑
- SQLite 例外:它默认
ROLLBACK模式下不提供语句级快照,高并发写入时建议显式用BEGIN IMMEDIATE避免database is locked
别指望索引或事务锁来“固定”窗口结果
有人试图给 (dept, salary) 加联合索引,或在查询前对整张表加 SELECT ... FOR UPDATE,这是徒劳的:
- 索引只影响性能,不改变
ORDER BY的语义;没决胜列时,索引再好结果照样飘 -
FOR UPDATE锁住的是行,不是“排序顺序”。窗口函数仍按你写的ORDER BY执行,锁不能让重复值自动变唯一 - 真正可控的只有
OVER子句本身:确保PARTITION BY字段存在、ORDER BY含唯一决胜列、避免在WHERE中误用窗口别名











