用lag()获取前两日价格后逐行比较可判断连续三天下跌,需建date或(symbol,date)复合索引,将时间过滤置于外层子查询,并避免select中叠加多个窗口函数。

怎么用 LAG() 和 CASE 判断连续三天下跌
核心是把“连续下跌”拆成:当天比前一天跌、前天比大前天跌、当天比大前天也跌——但更稳妥的做法是用 LAG() 拿出前两行值,再逐行比较。直接用 LAG(price, 1) 拿前一日价格,LAG(price, 2) 拿前两日价格,然后判断是否满足 price 。
注意点:
-
LAG()默认返回NULL(当没有前一行时),所以必须加WHERE过滤掉前两行,否则比较会失败或结果错乱 - 排序必须严格按时间升序(
ORDER BY date ASC),否则LAG()取的不是“前一日” - 日期字段不能有空缺(比如节假日跳过),否则“连续”在业务上不成立;如有空缺,得先生成完整日期序列再左连接
ROW_NUMBER() 配合分组识别连续段为什么更可靠
当数据里存在多段连续下跌(比如连续5天下跌),只标出“第三天”不够,可能需要整段起点和终点。ROW_NUMBER() 能帮我们构造“连续标识”:先标记每行是否下跌(1/0),再用 ROW_NUMBER() OVER (ORDER BY date) - ROW_NUMBER() OVER (PARTITION BY is_down ORDER BY date) 计算差值——相同差值的行就属于同一连续段。
实操关键:
- 先用子查询或 CTE 算出每行的
is_down = CASE WHEN price - 两次
ROW_NUMBER()的ORDER BY必须完全一致,否则差值无意义 - 这个差值本身没业务含义,仅作分组依据;后续再对每组
MIN(date)、MAX(date)就能拿到起止
PostgreSQL vs MySQL 8.0+ 的 LAG() 兼容性差异
语法基本一致,但 MySQL 8.0+ 要求 ORDER BY 显式写在窗口定义里(OVER (ORDER BY date)),而 PostgreSQL 允许省略(但不推荐);另外 MySQL 对 NULL 比较更敏感——price 在前一行是 <code>NULL 时返回 NULL,不是 FALSE,所以必须用 IS TRUE 或显式 AND LAG(price) IS NOT NULL。
常见错误现象:
- MySQL 中写
WHERE price 结果为空——因为比较结果是 <code>NULL,被WHERE当作不满足 - PostgreSQL 中若未设
PARTITION BY且数据跨多个股票代码,LAG()会跨股票混取,必须加PARTITION BY symbol - SQL Server 的
LAG()默认第三个参数是NULL,但 Oracle 需显式写DEFAULT NULL,否则报错
性能陷阱:窗口函数在大数据量下为什么变慢
LAG() 和 ROW_NUMBER() 都需全量排序,如果表没在 date 字段建索引,执行计划里会出现 Sort 节点,百万级数据可能秒级延迟。更隐蔽的问题是:如果 WHERE 条件写在窗口函数外(比如先 WHERE date >= '2024-01-01' 再套窗口),优化器未必能下推,导致先算全表再过滤。
提速建议:
- 确保
ORDER BY字段(通常是date或(symbol, date))有复合索引 - 把时间范围过滤放在最外层子查询里,而不是窗口函数内部
- 避免在
SELECT里同时用多个窗口函数(如LAG+ROW_NUMBER+SUM() OVER),它们各自触发一次排序;可考虑用 CTE 分步计算
连续下跌识别本身逻辑不复杂,但实际跑起来卡在哪,往往不是写法,而是索引缺失或数据没分区。尤其当表按日分区但查询跨多月时,记得确认分区裁剪是否生效。










