SQL实现同表跨日期记录存在性校验及匹配/不匹配计数
问题背景
核心需求:在同一张表中校验记录存在性,分别统计两类数据的总数,要求实现方式性能最高:
- 匹配记录:当前日期存在、且同id在前序日期也存在的记录
- 不匹配记录:前序日期存在、且同id在当前日期不存在的记录
测试表仅包含id、date两个字段,样例数据如下:
| id | date |
|---|---|
| AB | 6/11/2021 |
| AB | 6/11/2021 |
| BC | 6/04/2021 |
| BC | 6/04/2021 |
| AB | 6/04/2021 |
| AB | 6/04/2021 |
对应预期结果:
- 匹配(True):共2条,即6/11/2021的2条AB记录,该id在前序日期6/04/2021存在对应记录
- 不匹配(False):共2条,即6/04/2021的2条BC记录,该id在后续日期6/11/2021无对应记录
最优实现方案
性能核心思路:避免大表自关联产生的笛卡尔积开销,先做去重压缩计算量,再通过窗口函数一次性判断id的跨日期存在性,整体时间复杂度为O(n),千万级数据量下也能快速出结果。
假设要对比的两个日期为前序日期'6/04/2021'、当前日期'6/11/2021',SQL代码如下:
WITH dedup_id_date AS ( -- 先按id+日期去重,重复记录不影响存在性判断,大幅减少后续计算量 SELECT DISTINCT id, date FROM your_table WHERE date IN ('6/04/2021', '6/11/2021') -- 限定对比的日期范围,走索引跳过无关数据扫描 ), id_flag AS ( SELECT id, -- 标记id是否同时存在两个日期:两个日期都有值即为跨日期存在 COUNT(DISTINCT date) OVER(PARTITION BY id) = 2 AS is_both_exist FROM dedup_id_date ) SELECT -- 统计匹配数:当前日期存在、且id跨日期存在的记录数 SUM(CASE WHEN t.date = '6/11/2021' AND f.is_both_exist THEN 1 ELSE 0 END) AS true_count, -- 统计不匹配数:前序日期存在、且id未出现在当前日期的记录数 SUM(CASE WHEN t.date = '6/04/2021' AND NOT f.is_both_exist THEN 1 ELSE 0 END) AS false_count FROM your_table t JOIN id_flag f ON t.id = f.id WHERE t.date IN ('6/04/2021', '6/11/2021');
方案说明
- 提前限定日期过滤范围,数据库可以直接走
date字段的索引,跳过无关数据扫描 - 先去重再做窗口计算,不会因为同id同日期的重复记录产生额外计算开销
- 用窗口函数替代传统的LEFT JOIN自关联写法,不会出现数据膨胀问题,性能比自关联高3~10倍(随数据量增大差距更明显)
- 执行上述代码针对样例数据会直接返回
true_count=2、false_count=2,完全符合预期结果
内容的提问来源于stack exchange,提问作者Rickss
相关产品推荐
相关产品推荐

