用LAG()和日期差判断连续性断裂点缺失的日期区间本质是相邻记录之间date差值大于1天;核心思路是对日期列排序后用LAG(date)取上一行日期,再计算差值,结果>1即存在空档,需注意ORDER BY、NULL处理、日期类型统一及业务定义。

用 LAG() 和日期差判断连续性断裂点
缺失的日期区间本质是相邻记录之间 date 差值大于 1 天。核心思路是:对日期列排序后,用 LAG(date) 拿到上一行日期,再计算 date - LAG(date)(PostgreSQL/Oracle)或 DATEDIFF(day, LAG(date), date)(SQL Server)。只要结果 > 1,就说明中间有空档。
注意点:
- 必须先
ORDER BY date,否则LAG()返回无意义 - 首行
LAG()返回NULL,需用WHERE过滤掉或用COALESCE处理 - 日期类型要统一(避免
datetime带时分秒导致差值为 0.999)
构造完整日期序列再 LEFT JOIN 找空缺
当缺失范围可能很大(比如跨月、跨年),或需要列出所有空缺日而非仅断点时,更可靠的做法是先生成目标时间范围内的完整日期序列,再和原始表 LEFT JOIN,找出 NULL 行。
生成序列的方式因数据库而异:
- PostgreSQL:
GENERATE_SERIES('2024-01-01'::DATE, '2024-12-31'::DATE, '1 day') - SQL Server:
master.dbo.spt_values(type='P')配合DATEADD,或用递归 CTE - MySQL 8.0+:
WITH RECURSIVE dates AS (...)
关键陷阱:生成的序列必须覆盖原始数据的最小/最大日期边界,否则会漏掉两端的缺失区间。
合并连续空缺日为区间(start_date / end_date)
直接查出的空缺日是离散的,实际需求常要压缩成区间。这时要用「分组识别连续段」技巧:用 ROW_NUMBER() 减去日期本身的序号(如 date - INTERVAL '1 day' * ROW_NUMBER() OVER (ORDER BY date)),相同结果即属同一连续段。
示例逻辑(PostgreSQL):
SELECT MIN(date) AS start_date, MAX(date) AS end_date
FROM (
SELECT date,
date - INTERVAL '1 day' * ROW_NUMBER() OVER (ORDER BY date) AS grp
FROM missing_dates
) t
GROUP BY grp;注意:ROW_NUMBER() 必须和外层排序一致;若用 DATEDIFF 计算偏移量,需确保单位统一(天数)。
性能与边界情况提醒
大表场景下,LAG() 方案通常比生成全量日期序列快,但无法发现早于最小日期或晚于最大日期的缺失(比如数据从 1 月 10 日开始,但业务要求从 1 月 1 日起算)。
容易被忽略的点:
- 时区问题:跨时区写入的日期可能在本地时区下显示不连续
- 业务日历:周末/节假日是否算“缺失”需按需过滤(比如先排除周六日再检测)
- 主键重复:同一日期多条记录会导致
LAG()跳过真实空缺,建议先DISTINCT ON (date)或GROUP BY date
真正难的不是写出窗口函数,而是定义清楚“缺失”对当前业务意味着什么——是数据未采集?还是系统故障?这个定义直接决定 WHERE 条件和日期边界怎么设。

















