不能直接拿到历史最低值;必须显式指定rows between unbounded preceding and current row并配合order by(如date, id)确保逐行滚动计算,否则默认整列或range框架会导致结果错误。

MIN OVER 能直接拿到历史最低值吗?
不能直接用 MIN() OVER() 得到“从第一行到当前行”的历史最低值——默认窗口帧是 UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING,也就是整列取最小,结果每行都一样。要拿“历史最低”,必须显式指定窗口范围。
怎么写才能让 MIN OVER 算历史最低?
关键在 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW。这个帧定义告诉数据库:“只看从开头到当前这一行”。不同数据库语法一致,但 PostgreSQL/SQL Server/Oracle/BigQuery 都支持;MySQL 8.0+ 也支持。
-
MIN(price) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)—— 按时间顺序滚动求最小 - 如果
date有重复,建议加二级排序:ORDER BY date, id,避免非确定性结果 - 别漏掉
ORDER BY子句:没它,窗口帧默认是整分区,ROWS就失效
为什么有时候结果不对?常见坑有哪些?
最常踩的三个坑:
- 忘记
ORDER BY:写了ROWS却没排序,窗口逻辑退化成全表最小值 - NULL 值干扰:如果
price是NULL,MIN()会跳过它——但如果首几行全是NULL,历史最低值也会是NULL,不是 0 或空字符串 - 分区误用:加了
PARTITION BY user_id后,每个用户独立算历史最低,这没错;但若本意是全局历史,却加了分区,结果就全错了
替代方案:不用窗口函数能行吗?
能,但不推荐。比如自连接或子查询:(SELECT MIN(t2.price) FROM trades t2 WHERE t2.date 。性能差很多,数据量一过万就明显变慢;而且没法高效处理并行写入或实时流场景。窗口函数是标准解法,只要写对帧和排序,就是最稳最可读的路径。
真正要注意的,其实是排序字段的唯一性和时序可靠性——比如用 created_at 但精度只有秒级,又没加主键兜底,就可能让同秒多条记录的“历史最低”顺序不可控。










