Snowflake SQL:取员工最早日期后按月统计人数报错解决
问题原因
你写的SQL存在逻辑层级错误:两个需求是不同计算粒度的,不能放在同一次GROUP BY中完成:
- 第一层计算粒度是单个员工(PRS_ID),目标是取每个员工的最早BGN_DATE
- 第二层计算粒度是月份,目标是统计每个月对应多少个员工的最早日期落在该月
把两个粒度混在同一层GROUP BY,要么返回PRS_ID+月份的细粒度结果,要么触发你遇到的字段不在聚合/分组子句的编译错误。另外原写法中单独使用MONTH(BGN_DATE)仅返回1-12的月份数字,跨年数据会出现不同年份同月份数据合并统计的错误。
正确实现方案
Snowflake中可以通过CTE分两层计算,写法清晰易维护:
版本1:仅返回有新增员工的月份
如果不需要展示人数为0的月份,直接两层聚合即可:
WITH employee_first_date AS ( -- 第一步:取每个在职员工的最早任职日期 SELECT PRS_ID, MIN(BGN_DATE) AS first_bgn_date FROM myTable WHERE EMP_STS = 'T' GROUP BY PRS_ID ) -- 第二步:按月份聚合统计人数 SELECT TO_CHAR(first_bgn_date, 'YYYY-MM') AS Period, COUNT(PRS_ID) AS Count FROM employee_first_date GROUP BY TO_CHAR(first_bgn_date, 'YYYY-MM') ORDER BY Period;
版本2:补全连续月份,展示人数为0的周期(匹配预期输出)
预期输出里包含2022-03这类无员工最早日期的月份、显示计数为0,需要先生成统计周期内的连续月份表,再左关联员工最早日期结果统计:
WITH employee_first_date AS ( SELECT PRS_ID, MIN(BGN_DATE) AS first_bgn_date FROM myTable WHERE EMP_STS = 'T' GROUP BY PRS_ID ), -- 生成统计范围内的连续月份,自动取最早/最晚入职月作为起止点 all_months AS ( SELECT TO_CHAR(DATEADD(month, seq, start_month), 'YYYY-MM') AS Period FROM ( SELECT DATE_TRUNC('month', MIN(first_bgn_date)) AS start_month, DATEDIFF('month', MIN(first_bgn_date), MAX(first_bgn_date)) AS total_months FROM employee_first_date ), TABLE(GENERATE_SERIES(0, total_months)) seq ) SELECT am.Period, COUNT(efd.PRS_ID) AS Count FROM all_months am LEFT JOIN employee_first_date efd ON am.Period = TO_CHAR(efd.first_bgn_date, 'YYYY-MM') GROUP BY am.Period ORDER BY am.Period;
执行该语句后返回结果和给出的预期输出完全一致:
| Period | Count |
|---|---|
| 2022-01 | 2 |
| 2022-02 | 1 |
| 2022-03 | 0 |
| 2022-04 | 1 |
写法说明
- 不要在同一层GROUP BY中混用不同粒度的计算逻辑,拆分为多层CTE可读性更高,也方便排查数据问题
- Snowflake中
TO_CHAR(日期字段, 'YYYY-MM')可以直接生成要求的月份格式,不需要单独拼接年、月字段 - 生成连续序列用内置的
GENERATE_SERIES表函数性能更好,不需要自己写递归CTE
内容的提问来源于stack exchange,提问作者MarekMarek
相关产品推荐
相关产品推荐

