大屏数据有一个折线图,需要近 30 天的和近 12 个月的每月记录数统计。

一开始想的是数据先按照天或者月统计完数据之后,再通过排序解决:

List<DistributionDTO> distributionDTOList = statisticService.selectCarDistribution();
distributionDTOList.sort(Comparator.comparing(DistributionDTO::getDate));

后来考虑到分组查询的不足,如果当月没有记录,那么当月的统计项就会缺失,这与想要的结果不符。

谷歌到 Mysql统计近 30 天的数据,无数据的填充 0,里面提到了生成 n 天日期的 sql 语句:

SELECT
    @s := @s + 1 AS indexs,
    DATE_FORMAT( DATE( DATE_SUB( CURRENT_DATE, INTERVAL @s DAY ) ), '%Y-%m-%d' ) AS dates 
FROM
    mysql.help_topic,
    ( SELECT @s := -1 ) temp  #不想包含当天,@s:=0
WHERE
    @s < 30 
ORDER BY
    dates 

因为变量 @s 是从 -1 + 1 = 0 开始循环生成的,所以生成的日期从当天开始(包含当天)往前数,一直到 30 不满足条件(类似循环里的先执行再循环判断 do..while)才结束。所以上面的查询其实生成了近 31 天日期。

如果只要近 30 天数据(包含当天),则需要 where @s < 30 改成 where @s < 29;
如果不想包含当天,则将 @s 初始值设置为 0,即 select @s := 0

另外一篇里 查询近 n 天的日期(以查询最近七天为例),只有 FROM 后跟的表名不一样,为 information_schema. TABLES,不了解有什么区别。

主要生成日期的函数 DATE_FORMAT( DATE( DATE_SUB( CURRENT_DATE, INTERVAL @s DAY ) ), '%Y-%m-%d' ),其中最外层为将日期格式化(我这边是 datetime 类型),这里转化为 年-月-日 格式;再往里是日期转化函数 DATE,大概是将时间戳转为日期;最里面的 DATE_SUB 看名称 sub 是减,第一个参数 CURRENT_DATE 返回当前的日期,盲猜第二个参数是减多少天。

菜鸟教程里 MySQL DATE_SUB() 函数 定义:DATE_SUB() 函数从日期减去指定的时间间隔。第二个参数可以取很多,年、月、日、小时、分钟、秒、毫秒等,所以转化为月份统计就是 INTERVAL @s MONTH

最后,将当前的日期临时表和记录数分组查询的临时表进行左联:

SELECT
    date_table.dates AS date_value,
    IFNULL( temp.count, 0 ) AS num 
FROM
    (
    SELECT
        @s := @s + 1 AS indexs,
        DATE_FORMAT( DATE ( DATE_SUB( CURRENT_DATE, INTERVAL @s DAY ) ), '%Y-%m-%d' ) AS dates 
    FROM
        mysql.help_topic,
        ( SELECT @s := - 1 ) temp #不想包含当天,@s:=0
        
    WHERE
        @s & lt;
    29 
    ORDER BY
        dates 
    ) date_table
LEFT JOIN (
    SELECT LEFT
        ( created_at, 10 ) AS date_value,
        SUM( wireline_num ) AS count 
    FROM
        biz_work_order t1 
    WHERE
        t1.order_state = 7 
    GROUP BY
        LEFT ( created_at, 10 ) 
    ) temp ON date_table.dates = temp.date_value 
ORDER BY
    date_table.dates ASC

其中的 LEFT 函数,就是取左边多少位,用在这里因为存储的是 datetime,日期+时间,需要截取日期部分,才能左联。