ORDER BY缺失或不唯一会导致窗口函数结果不可靠;必须补唯一字段如ORDER BY salary DESC, employee_id,并确保PARTITION BY字段合理且有联合索引。

ORDER BY 缺失或不唯一,导致排名/累计值不可靠
窗口函数一旦涉及顺序依赖(比如 ROW_NUMBER()、SUM() OVER (... ORDER BY ...)、LAG()),没写 ORDER BY 或排序字段不唯一,结果就可能每次执行都不一样——尤其在 PostgreSQL、Oracle 中更明显。这不是“偶尔出错”,而是逻辑缺陷。
-
LAG(amount) OVER (PARTITION BY customer_id)→ 没ORDER BY,数据库自由决定“前一行”是谁,结果随机 -
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary)→ 若多人同薪,顺序不确定,排名会漂移 - 正确做法:补上唯一字段,如
ORDER BY salary DESC, employee_id或ORDER BY created_at, log_id
PARTITION BY 字段选择不当,引发数据倾斜或全表扫描
用高基数(如 user_id)或低基数(如 status IN ('A','B'))字段做 PARTITION BY,都可能让查询变慢。前者产生海量小分区,调度开销大;后者只剩 2–3 个超大分区,CPU 和内存全压在少数节点上。
- 先检查分布:
SELECT department_id, COUNT(*) FROM employees GROUP BY department_id ORDER BY 2 DESC LIMIT 5; - 避免对无业务意义的字段分区,比如
PARTITION BY EXTRACT(YEAR FROM order_date)而不加过滤,会拉全量年份数据 - 大表上务必确保
PARTITION BY+ORDER BY字段有联合索引,例如CREATE INDEX idx_user_time ON orders(user_id, order_date) INCLUDE(amount);
误在 WHERE 或 HAVING 中直接引用窗口函数别名
窗口函数在 SQL 执行顺序中晚于 WHERE 和 GROUP BY,所以 WHERE rank 这种写法会报错:<code>column "rank" does not exist。这不是语法糖没生效,而是阶段根本还没到。
- 错误:
SELECT ..., RANK() OVER (...) AS rank FROM t WHERE rank - 正确:用 CTE 或子查询封装,再过滤:
WITH ranked AS (SELECT ..., RANK() OVER (...) AS rank FROM t) SELECT * FROM ranked WHERE rank - 注意:CTE 不是“万能解药”,PostgreSQL 中非物化 CTE 仍可能重复计算,必要时加
MATERIALIZED(v12+)
ROWS vs RANGE 混用,性能差十倍不止
ROWS BETWEEN 按物理行数切片,快;RANGE BETWEEN 按值范围匹配,慢。尤其在时间类窗口(如“过去7天”)里,用 RANGE BETWEEN INTERVAL '7 days' PRECEDING AND CURRENT ROW 看似自然,实则每行都要全扫分区找匹配值,无法走索引。
- 推荐方案:先按日/小时归一化时间字段(如
sale_date::date),再用ROWS BETWEEN 6 PRECEDING AND CURRENT ROW -
SUM(sales) OVER (ORDER BY day ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)→ 安全高效 -
SUM(sales) OVER (ORDER BY ts RANGE BETWEEN INTERVAL '1 hour' PRECEDING AND CURRENT ROW)→ 小数据可忍,千万级必卡
实际跑报表时,最常被忽略的是:窗口函数的输入数据集大小,由 WHERE 和 JOIN 决定,不是由 OVER 子句控制。哪怕你只想要 TOP 10,也得先用 WHERE 把无关年份、状态、租户的数据筛掉——否则窗口计算照样扛着十亿行跑。

















