不能直接用LAG()算连续天数,因其按物理行序取值而无法校验交易日是否真实连续,易将休市后的上涨误判为连续上涨;SELF JOIN通过INNER JOIN和日期+1条件确保前一天存在且为交易日,再在ON子句中判断价格上涨,结合递归CTE可求最长连续上涨段。

为什么不能直接用LAG()或窗口函数算连续天数?
因为窗口函数在遇到数据缺失(比如节假日休市)时,LAG()会跳过空行但不校验交易日是否真正连续——它只按查询结果的物理顺序取上一行,而股票行情表里往往缺节假日数据。结果就是把“隔了3天休市后的上涨”误判为“连续4天上涨”。SELF JOIN 的优势在于能显式限定“前一天必须存在且是交易日”,逻辑更可控。
SELF JOIN 需要哪些基础表结构和条件?
假设你有一张 stock_price 表,至少包含:stock_code、trade_date(DATE 类型)、close_price。关键点不是连接本身,而是连接条件的设计:
- 必须用
INNER JOIN,排除任意一天缺失导致链断裂的情况 - 连接条件得写成:
t1.trade_date = t2.trade_date + INTERVAL 1 DAY(MySQL)或t1.trade_date = t2.trade_date + 1(PostgreSQL/Oracle,注意日期类型支持) - 上涨判断必须放在
ON子句里(不是WHERE),否则会漏掉首日:ON t1.stock_code = t2.stock_code AND t1.close_price > t2.close_price - 务必加索引:
(stock_code, trade_date),否则多层自连接性能暴跌
怎么递归查出“最长连续上涨段”的长度?
SELF JOIN 本身只能查两日关系,要算连续天数得构造“链式连接”。例如查3连涨,就得三表自连接:t0 → t1 → t2,每步都满足上涨+日期连续。但硬写 N 层 JOIN 不现实。更可行的是用递归 CTE(MySQL 8.0+/PostgreSQL):
WITH RECURSIVE up_chain AS (
-- 初始:每个交易日作为潜在起点
SELECT stock_code, trade_date, close_price, 1 AS days
FROM stock_price sp1
WHERE NOT EXISTS (
SELECT 1 FROM stock_price sp2
WHERE sp2.stock_code = sp1.stock_code
AND sp2.trade_date = sp1.trade_date - INTERVAL 1 DAY
AND sp2.close_price uc.close_price
)
SELECT stock_code, MAX(days) AS max_consecutive_up_days
FROM up_chain
GROUP BY stock_code;
注意:递归深度默认可能不够,MySQL 需设 cte_max_recursion_depth;PostgreSQL 用 MAX_RECURSION 1000 参数。
实际跑出来天数对不上?先检查这三件事
常见偏差几乎都来自数据质量或边界处理:
-
trade_date是字符串还是 DATE?字符串比较(如 '2023-01-02' > '2023-01-01')可能因排序规则失效,必须转为日期类型 - 复权问题:如果没用前复权价,除权日次日价格跳空下跌会被误判为“上涨中断”,实际应统一用
adj_close - 同一股票同日多条记录(如分笔成交汇总进日线表)会导致自连接爆炸式膨胀,先用
GROUP BY stock_code, trade_date去重
连续天数本质是“日期连续性 + 价格单调性”的交集,SELF JOIN 强在可精确控制这两个条件的耦合方式,但代价是写法比窗口函数绕。真要跑全市场,建议先按 stock_code 分批处理,避免单次递归太深。











