SQL如何实现按每个月份统计对应ID过去6个月的出现次数
实现方案
完全可以用自连接实现,这也是对SQL初学者来说逻辑最直观的写法,另外也提供性能更好的窗口函数写法,你可以根据自己使用的数据库选择。
首先统一前提:假设你的表名为target_table,包含两个核心字段:
id:需要统计的主体IDstat_month:月份字段,为日期类型(建议统一存每个月第一天,例如2023-01-01代表2023年1月,不建议用字符串/数字格式存月份,会大幅增加日期计算的出错概率)
默认统计规则:对每条记录,取包含当月在内的近6个自然月范围内,同ID的记录出现次数,和你给出的示例结果逻辑一致。
方法1:自连接实现
核心逻辑是把同一张表取两个别名使用:主表返回所有原始记录,连接后的副表用来匹配符合时间范围的历史记录,最后分组计数即可。
SELECT t1.id, t1.stat_month, COUNT(t2.stat_month) AS cnt_6month FROM target_table t1 LEFT JOIN target_table t2 ON t1.id = t2.id -- 时间范围:当前记录月份往前推5个月到当月,总共6个自然月 AND t2.stat_month >= DATE_SUB(t1.stat_month, INTERVAL 5 MONTH) AND t2.stat_month <= t1.stat_month GROUP BY t1.id, t1.stat_month ORDER BY t1.id, t1.stat_month;
不同数据库的日期计算函数需要做对应替换:
- PostgreSQL:把
DATE_SUB部分换成t2.stat_month >= t1.stat_month - INTERVAL '5 months'- SQL Server:把
DATE_SUB部分换成t2.stat_month >= DATEADD(month, -5, t1.stat_month)
如果你的表存在同一个ID同一个月多条记录的情况,这个写法也能正常统计,不需要额外去重。
方法2:窗口函数实现(数据量大时优先选)
如果你用的数据库支持窗口函数(MySQL8.0及以上、PostgreSQL、SQL Server、主流大数据组件都支持),可以直接用带滑动窗口的聚合写法,不需要做表连接,性能比自连接高很多:
SELECT id, stat_month, COUNT(*) OVER ( PARTITION BY id ORDER BY stat_month ROWS BETWEEN 5 PRECEDING AND CURRENT ROW ) AS cnt_6month FROM target_table ORDER BY id, stat_month;
注意:这个写法有适用前提:每个ID对应的月份是连续无缺失、且每个自然月最多只有1条记录。如果存在缺月、或者单月多条记录的情况,还是用自连接的日期范围匹配逻辑更稳妥,避免统计窗口偏移。
注意事项
- 先确认业务侧的时间范围规则:如果是精确回溯6个月的时间跨度(例如2023-06-15的记录统计2023-01-15之后的数据),只需要调整上面日期条件里的INTERVAL值为6个月,同时把上界改成小于当前记录日期即可。
- 如果你的
Month字段目前是字符串(例如'2023-06')或者数字(例如202306)格式,建议先转成原生日期类型再做计算,不然需要额外处理跨年、月份借位的逻辑,很容易出bug。
内容的提问来源于stack exchange,提问作者user11429634
相关产品推荐
相关产品推荐

