窗口函数需先过滤再开窗,避免全表扫描与内存溢出;where条件须命中复合索引前缀,partition by和order by字段需联合索引;sum() over累计时应提前转bigint防int溢出;深分页优先用游标替代offset。

窗口函数必须先过滤再开窗
窗口函数本身不支持下推WHERE条件,直接在全表上用ROW_NUMBER()或SUM() OVER,等于主动申请内存溢出。数据库会把整个分区数据拉进内存排序或累计,哪怕你只取前10行。
- 错误写法:
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) rn FROM orders→ 全表扫描 + 全表排序 - 正确写法:
SELECT *, ROW_NUMBER() OVER (ORDER BY created_at DESC) rn FROM (SELECT id, user_id, created_at FROM orders WHERE status = 'shipped' AND created_at >= '2026-04-01') t - WHERE条件要命中复合索引前缀,例如
(status, created_at),让子查询走range扫描而非ALL
PARTITION BY和ORDER BY字段必须建联合索引
没有索引的ORDER BY字段,数据库只能filesort;没覆盖PARTITION BY列的单列索引无效——分区键不参与查找,优化器照样全量加载。
- 黄金索引结构:
CREATE INDEX idx_user_order ON orders(user_id, order_date) INCLUDE(amount)(PostgreSQL) - MySQL不能用
INCLUDE,改用宽索引:INDEX(user_id, order_date, amount),避免回表 - EXPLAIN后看到
WindowAgg在最外层、且Sort Method: external merge Disk(PG)或Using filesort(MySQL),基本确认没走索引
SUM() OVER累计求和要防INT隐式溢出
SUM() OVER本身不溢出,但对INT列累计时,中间结果仍按INT计算,超2147483647就报错或截断。关键不是窗口语法,而是输入表达式的类型决定了聚合计算类型。
- 必须提前转大类型:
CAST(amount AS BIGINT)或amount::BIGINT,别写CAST(SUM(amount) AS BIGINT)(晚了) - 如果加了INT常量(如
+ 1),也要一并转:CAST(amount AS BIGINT) + CAST(1 AS BIGINT) -
ORDER BY必须显式写,且含唯一列防重复值排序不确定性,例如:SUM(CAST(amount AS BIGINT)) OVER (ORDER BY created_at, id)
能不用窗口函数就别用,优先索引+LIMIT
90%分页场景根本不需要全局序号。比如「查最新100笔已发货订单」,ORDER BY created_at DESC LIMIT 100走索引就能秒出,比算全量ROW_NUMBER()快两个数量级。
-
ROW_NUMBER() OVER (ORDER BY x) LIMIT 10 OFFSET 10000是典型陷阱:OFFSET仍需跳过前10000行,窗口函数照样全量排序 - 深分页改用游标式:
WHERE created_at ,取上一页最后一条的<code>created_at值 - 视图里写
ORDER BY会导致物化临时表全量排序,再被外层LIMIT截断——排序逻辑应由调用方控制











