You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

日期区间内包含缺失日期、月份的统计SQL实现需求

Hey,我明白你的需求——你需要统计指定区间内所有日期/月份,哪怕那天/那个月没有数据也要显示0,对吧?原SQL的问题在于它只会返回cupons表中有记录的日期/月份,那些空的日期会被漏掉。下面分两种情况给你解决方法:

1. 日期统计(包含指定区间内所有日期,无数据时显示0)

你的原SQL只能返回有数据的日期,因为GROUP BY是基于表中存在的记录分组的。要包含所有日期,我们需要先生成一个覆盖整个区间的日期序列,再和统计结果做左连接。

方案1:MySQL 8.0+ 用递归CTE生成日期序列(推荐)

WITH RECURSIVE date_range AS (
    SELECT '2017-02-02' AS date_val
    UNION ALL
    SELECT DATE_ADD(date_val, INTERVAL 1 DAY)
    FROM date_range
    WHERE date_val < '2018-05-04'
)
SELECT 
    IFNULL(c.num, 0) AS num,
    DATE_FORMAT(d.date_val, '%d/%m/%Y') AS data
FROM date_range d
LEFT JOIN (
    SELECT 
        COUNT(*) AS num,
        DATE_FORMAT(c.dataCupo, '%Y-%m-%d') AS date_key
    FROM cupons c
    WHERE 
        c.dataCupo BETWEEN '2017-02-02' AND '2018-05-04'
        AND c.proveidor != 'VINCULADO'
        AND c.empresa = 1
    GROUP BY date_key
) c ON d.date_val = c.date_key
ORDER BY d.date_val;

说明:

  • 递归CTE date_range会生成从2017-02-02到2018-05-04的每一天;
  • 子查询先统计每个有数据日期的记录数,用%Y-%m-%d作为日期键来匹配;
  • 左连接确保所有日期都出现在结果中,没有数据的日期num会显示0;
  • 最后按日期排序保证结果顺序正确。

方案2:MySQL 5.x 用数字表生成日期序列

如果你的MySQL版本不支持递归CTE,可以用数字表来生成日期:

SELECT 
    IFNULL(c.num, 0) AS num,
    DATE_FORMAT(d.date_val, '%d/%m/%Y') AS data
FROM (
    SELECT 
        DATE_ADD('2017-02-02', INTERVAL n DAY) AS date_val
    FROM (
        SELECT a.N + b.N * 10 + c.N * 100 AS n
        FROM 
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a,
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) b,
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4) c
        WHERE a.N + b.N * 10 + c.N * 100 <= DATEDIFF('2018-05-04', '2017-02-02')
    ) nums
) d
LEFT JOIN (
    SELECT 
        COUNT(*) AS num,
        DATE_FORMAT(c.dataCupo, '%Y-%m-%d') AS date_key
    FROM cupons c
    WHERE 
        c.dataCupo BETWEEN '2017-02-02' AND '2018-05-04'
        AND c.proveidor != 'VINCULADO'
        AND c.empresa = 1
    GROUP BY date_key
) c ON d.date_val = c.date_key
ORDER BY d.date_val;
2. 月份统计(包含指定区间内所有月份,无数据时显示0)

和日期统计的思路一样,先生成所有月份的序列,再左连接统计结果,确保没有数据的月份也能显示0。

方案1:MySQL 8.0+ 用递归CTE生成月份序列

WITH RECURSIVE month_range AS (
    SELECT DATE_FORMAT('2017-02-02', '%Y-%m-01') AS month_val
    UNION ALL
    SELECT DATE_ADD(month_val, INTERVAL 1 MONTH)
    FROM month_range
    WHERE month_val <= DATE_FORMAT('2018-05-04', '%Y-%m-01')
)
SELECT 
    IFNULL(c.num, 0) AS num,
    DATE_FORMAT(m.month_val, '%m/%Y') AS data
FROM month_range m
LEFT JOIN (
    SELECT 
        COUNT(*) AS num,
        DATE_FORMAT(c.dataCupo, '%Y-%m') AS month_key
    FROM cupons c
    WHERE 
        c.dataCupo BETWEEN '2017-02-02' AND '2018-05-04'
        AND c.proveidor != 'VINCULADO'
        AND c.empresa = 1
    GROUP BY month_key
) c ON DATE_FORMAT(m.month_val, '%Y-%m') = c.month_key
ORDER BY m.month_val;

说明:

  • month_range生成区间内每个月的第一天(比如2017-02-01、2017-03-01...2018-05-01);
  • 子查询统计每个有数据月份的记录数,用%Y-%m作为月份键匹配;
  • 左连接保证所有月份都被包含,无数据的月份num为0。

方案2:MySQL 5.x 用数字表生成月份序列

SELECT 
    IFNULL(c.num, 0) AS num,
    DATE_FORMAT(m.month_val, '%m/%Y') AS data
FROM (
    SELECT 
        DATE_ADD(DATE_FORMAT('2017-02-02', '%Y-%m-01'), INTERVAL n MONTH) AS month_val
    FROM (
        SELECT a.N + b.N * 10 AS n
        FROM 
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) a,
            (SELECT 0 AS N UNION ALL SELECT 1) b
        WHERE a.N + b.N * 10 <= PERIOD_DIFF(DATE_FORMAT('2018-05-04', '%Y%m'), DATE_FORMAT('2017-02-02', '%Y%m'))
    ) nums
) m
LEFT JOIN (
    SELECT 
        COUNT(*) AS num,
        DATE_FORMAT(c.dataCupo, '%Y-%m') AS month_key
    FROM cupons c
    WHERE 
        c.dataCupo BETWEEN '2017-02-02' AND '2018-05-04'
        AND c.proveidor != 'VINCULADO'
        AND c.empresa = 1
    GROUP BY month_key
) c ON DATE_FORMAT(m.month_val, '%Y-%m') = c.month_key
ORDER BY m.month_val;

内容的提问来源于stack exchange,提问作者sergi castells

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 11:10:21