SQL CASE WHEN统计补0及免UNION按月查询实现方案咨询
问题解答
针对「不建物理维度表实现缺失组合补0」的问题
完全不需要单独创建持久化的维度表,直接在查询内用内联值枚举+笛卡尔积生成全量维度组合即可,所有逻辑都在单条查询内完成,没有额外表维护成本。
你需要覆盖的三个维度有效值可以通过以下方式快速生成:
- 地区维度:直接从业务表中
DISTINCT去重取所有存在的name值,不需要手动硬编码 - 版本维度:同理从业务表中
DISTINCT取所有存在的version值(你的场景下就是A、B两个值) - 月份维度:用递归CTE生成你要统计的时间范围内的所有月份起始日期,不需要手动逐月份录入
三个维度做笛卡尔积,就能得到所有「地区+月份+版本」的全量组合,再左关联你的业务统计结果,关联不上的count值直接补0即可。
针对「替代多段SELECT UNION冗余写法」的问题
根本不需要逐月份写独立查询再拼接,直接用日期函数把date_completed字段截断到对应月份的粒度,一次性按月份、地区、版本分组统计即可。后续调整统计时间范围只需要改筛选的起止时间,不需要新增/删除UNION段,维护成本极低。
可直接参考的代码示例(适用于MySQL 8.0+/PostgreSQL等支持CTE的数据库)
WITH RECURSIVE -- 生成统计范围内的所有连续月份,调整日期上下限即可修改统计范围 month_dim AS ( SELECT DATE('2021-01-01') AS stat_month UNION ALL SELECT DATE_ADD(stat_month, INTERVAL 1 MONTH) FROM month_dim WHERE stat_month < DATE('2021-02-01') ), -- 自动提取业务表内所有存在的地区 name_dim AS ( SELECT DISTINCT name FROM your_business_table ), -- 自动提取业务表内所有存在的版本 version_dim AS ( SELECT DISTINCT version FROM your_business_table ), -- 三个维度笛卡尔积生成全量维度组合 all_dim_comb AS ( SELECT nd.name, md.stat_month, vd.version FROM name_dim nd CROSS JOIN month_dim md CROSS JOIN version_dim vd ), -- 一次性完成全时间段的业务数据统计,无需逐月份写SELECT biz_stat AS ( SELECT name, DATE_FORMAT(date_completed, '%Y-%m-01') AS stat_month, -- PostgreSQL替换为DATE_TRUNC('month', date_completed)::DATE version, COUNT(id) AS record_cnt FROM your_business_table WHERE date_completed BETWEEN '2021-01-01' AND '2021-02-28 23:59:59' GROUP BY name, stat_month, version ) -- 全量维度左关联统计结果,空值统一补0 SELECT adc.name, adc.stat_month, adc.version, COALESCE(bs.record_cnt, 0) AS record_cnt FROM all_dim_comb adc LEFT JOIN biz_stat bs ON adc.name = bs.name AND adc.stat_month = bs.stat_month AND adc.version = bs.version ORDER BY adc.stat_month, adc.name, adc.version;
注意:如果使用不支持递归CTE的老版本MySQL(5.x),月份维度可以直接用内联值枚举的方式实现,比如
SELECT '2021-01-01' AS stat_month UNION ALL SELECT '2021-02-01' AS stat_month,维护成本依然远低于逐月份编写业务统计语句再UNION的方案。
内容的提问来源于stack exchange,提问作者Smc
相关产品推荐
相关产品推荐

