MySQL的跨年周统计问题(%X-%V)
MySQL的跨年周统计问题
最近在做项目的时候,发现了一个报表在进行周统计并且跨年的时候会出现问题,具体问题截图如下:(主要与%X-%V的时间格式符有关)
2016-12-18 ~~ 2017-01-13 周统计
图1
2015-12-20 ~~ 2016-01-17 周统计
图2
这两张图中,周统计中,代码中的sql时间格式化为
DATE_FORMAT(FROM_UNIXTIME(regTime,'%Y-%m-%d'),'%Y-%U')
为了让接下来的解释更加清楚,这里先贴一张MySQL的时间格式化符和描述
格式 | 描述 |
---|---|
%a | 缩写星期名 |
%b | 缩写月名 |
%c | 月,数值 |
%D | 带有英文前缀的月中的天 |
%d | 月的天,数值(00-31) |
%e | 月的天,数值(0-31) |
%f | 微妙 |
%H | 小时 (00-23) |
%h | 小时 (01-12) |
%I | 小时 (01-12) |
%i | 分钟,数值(00-59) |
%j | 年的天 (001-366) |
%k | 小时 (0-23) |
%l | 小时 (1-12) |
%M | 月名 |
%m | 月,数值(00-12) |
%p | AM 或 PM |
%r | 时间,12-小时(hh:mm:ss AM 或 PM) |
%S | 秒(00-59) |
%s | 秒(00-59) |
%T | 时间, 24-小时 (hh:mm:ss) |
%U | 周 (00-53) 星期日是一周的第一天 |
%u | 周 (00-53) 星期一是一周的第一天 |
%V | 周 (01-53) 星期日是一周的第一天,与 %X 使用 |
%v | 周 (01-53) 星期一是一周的第一天,与 %x 使用 |
%W | 星期名 |
%w | 周的天 (0=星期日, 6=星期六) |
%X | 年,其中的星期日是周的第一天,4 位,与 %V 使用 |
%x | 年,其中的星期一是周的第一天,4 位,与 %v 使用 |
%Y | 年,4 位 |
%y | 年,2 位 |
由表可以看出,%U是代表一周,并且是以周日当做第一天,由图2可以看出,15-12-27作为15年的最后一周,其7天的时间分别为12-27、12-28、12-29、12-30、12-31、16-01-01、16-01-02,也就是说,15年的最后一周跨到了16年,那么%U是怎么处理的呢?%U代表的是第0周开始的,使用星期天作为一周的第一天,当遇到本年的第一个星期天时,就是第 1 周了,那么在1.1到今年第一周之前的天数就当做今年的第0周
举例说明:
(1)2017的第一个周日 正好是2017-01-01,那么从这一天开始就是第 1 周,而不是第0周
(2) 2016的第一个周日,是 2016-01-03,那么2016-01-01至2016-01-03是算作第 0 周,而
把2013-01-03 至 2013-01-10 算作2013年的第 1 周
所以这里就有匪夷所思的第0周出现了,那么程序是如何格式化这个第0周的呢,这样的跨年现象,是把15年的最后一周变成只有12-27、12-28、12-29、12-30、12-31这5天,而16-01-01、16-01-02算做新的一周,也就是图2中从1.1开始算一周,到了16.1.3号又重新计算一周。换句话说,15年的最后一周的数据是图2报表中
15.12.27的数据加上16.01.01的数据。
因为说到了%U,所以这里再补充解释一下%u,%u:使用星期一作为一周的第一天,这个就不像上面一样了,这里并不是遇到每年的第一个周一算第 1 周,而是计算第一个周一之前的天数如果能超过3天(不包括3天),那么计算为第 1 周。否则就计算为第0周。
对于这样的跨年引发的周统计的问题,有没有更合理的解决方式呢?答案是有的,那就是MySQL的另外个两个格式符%X-%V
%X: 年,其中的星期日是周的第一天,4 位,与 %V 使用
%V: 周, (01-53) 星期日是一周的第一天,与 %X 使用
网上很少有关于%X,%V的使用方法的讲解,我也只是偶然中看到CSDN的一篇帖子才发现有这么个东西的存在,
%V 与 %U 一样的地方就是也是使用周日作为一周的开始,并且也是遇到第一个周日开始算作是第一周,但是这两个对这个下一年第一个周日之前的这几天处理不一样,%U是把下一年之前的几天算作第0周而%V是把下一年之前的几天算作上一年的最后一周,所以使用%X-%V这样的格式符就能解决图2的问题
这里也补充一下%v,%v与%u也有相似之处,就是同样适用周一作为 一周的开始,也同样计算每年第一个周一之前的
天数,如果>3天则记为第 1 周,不同是,如果≤3天,则算作上一年的最后一周
DATE_FORMAT(FROM_UNIXTIME(regTime,'%Y-%m-%d'),'%X-%V')
既然图2出现的跨年现象解决了,那么图1的现象是否也解决了呢?答案是不,为什么呢?分析图1和图2,我们可以看出,图2是因为上一年的最后一周跨到下一年了,所以导致有下一年的1.1这一周出现,但是图1不一样,16年的最后一周刚好到了12.31,并没有跨到17年,图一的17年第一周是从1.8开始的,1.1这一周没有显示出来。咦,这就奇怪啦,我是可以保证时间格式化是没有问题的,为什么还会出现这个问题呢?只有一个答案可以解释了,那么就是代码问题,如果是代码问题,那么就要追溯时间转换的方法上了,view代码。
问题就出现在这里了,因为转换周的原理是先求出1.1 + 周数 * 7 的日期,然后再减去1.1号距离第一个周日的天数 - 1,然后就得到某一周的开始的周日日期,具体公式为:(1.1 + 周数 * 7) - (1.1距离第一个周日的天数 - 1)
举例说明:
(1)2016年的1.1是周五,按周日为一周第一天这样来计算,周五就是一周的第6天,从1.1号开始,一周为7天。假设我们从第二周开始,1.1 + (周数 * 7)也就是 1.1 + 14 = 1.15,1.15 - (1.1距离第一个周日的天数 - 1)也就是1.15 - (6-1) = 1.10.所以1.10就是刚好是16年的第二个周的开始
(2)上面的例子对于大多数情况是适用的,但是如果一年的第一个周日刚好是1.1的话,上面的例子就会出现这样的情况,1.8 - (1 - 1) = 1.8,所以刚好1.1这一周的数据就被过滤掉了,所以如果刚好一年的第一个周日刚好是1.1的话,那么就应该是1.8 - 7 = 1.1,如下图所示
这是在处理项目跨年周统计的时候的总结,在第一点的时候也说到了MySQL的格式符问题,下面就想详细的解释一下MySQL几个格式符的问题
既然%Y表示年,%X也表示年,那么%X-%V是否可以写成%Y-%V呢?常用的表示年月的%Y-%m是否可以写成%X-%m呢?
# %X-%V
SELECT
DATE_FORMAT(FROM_UNIXTIME(regTime, '%Y-%m-%d'),'%X-%V'
) AS groupweek,count(distinct FROM_UNIXTIME(regTime, '%Y-%m-%d') ) from acctStatus
WHERE
regTime >UNIX_TIMESTAMP('2015-12-20 00:00:00') and regTime < UNIX_TIMESTAMP('2016-01-17 00:00:00')
AND regTime <> '' group by groupweek
结果为下图,查询正确
那如果写成%Y-%V,会发生什么情况呢?
# %Y-%V
SELECT
DATE_FORMAT(FROM_UNIXTIME(regTime, '%Y-%m-%d'),'%Y-%V'
) AS groupweek,count(distinct FROM_UNIXTIME(regTime, '%Y-%m-%d') ) from acctStatus
WHERE
regTime >UNIX_TIMESTAMP('2015-12-20 00:00:00') and regTime < UNIX_TIMESTAMP('2016-01-17 00:00:00')
AND regTime <> '' group by groupweek
结果如下图所示,会发现多出了一个诡异的2016年52周
分析原因:网上找不到合理的资料,我就根据我的理解分析下产生这个现象的原因,%Y的解释为年,%X的解释为年,其中的星期日是周的第一天,也就是说%Y是把1.1当做一年的第一天,所以%Y-%U会从第0周开始算起,而%X把第一个周日当做一周的第一天,也把第一个周日当做一年的第一天,所以和%V(周 (01-53) 星期日是一周的第一天,与 %X 使用刚好适用。而%Y与%V本身之间对于第一天的基准就是不同的,一起使用当然就会有问题,如果1.1距离第一个周日之间是没有数据的,那么我们不会发现这两种写法有什么区别,因为数据显示会是完全一样的。同理,来验证下%Y-%m与%X-%m的差别
# %Y-%m
SELECT
DATE_FORMAT(FROM_UNIXTIME(regTime, '%Y-%m-%d'),'%Y-%m'
) AS groupmonth,count(distinct FROM_UNIXTIME(regTime, '%Y-%m-%d') ) from acctStatus
WHERE
regTime >UNIX_TIMESTAMP('2015-12-20 00:00:00') and regTime < UNIX_TIMESTAMP('2016-01-17 00:00:00')
AND regTime <> '' group by groupmonth
结果如下图所示,查询结果正确
再来看%X-%m# %X-%m
SELECT
DATE_FORMAT(FROM_UNIXTIME(regTime, '%Y-%m-%d'),'%X-%m'
) AS groupmonth,count(distinct FROM_UNIXTIME(regTime, '%Y-%m-%d') ) from acctStatus
WHERE
regTime >UNIX_TIMESTAMP('2015-12-20 00:00:00') and regTime < UNIX_TIMESTAMP('2016-01-17 00:00:00')
AND regTime <> '' group by groupmonth
结果如下图所示,多出了一个2015-1,但是我们的查询条件却是从15.12开始的,也就是说2015-01这个数据是有问题的
总结
跨年的周统计要使用%X-%V,%Y要和%U、%m一起使用,%X要和%V一起使用,如果随意使用,就会产生错误,同理,
%x,%y,%u,%v也是一样的。
MySQL的跨年周统计问题(%X-%V)相关推荐
- mysql 周_MySQL的跨年周统计问题
在mysql中,如果要查询的表中只有日期字段,但是业务需求要按照周分组,排序的话,mysql提供了多种方法: 1.date_format DATE_FORMAT(date, format) 函数根据f ...
- mysql每个月数据按天行转列_Mysql 中使用DATE_FORMAT函数按月、周统计数据
项目中的统计报表作的很多,需求中有按周.月统计数据的.查看了Mysql的API,发现Date_format是格式化日期的,看了Date_format()的具体说明后就用这个函数按周统计,sql大致如下 ...
- mysql如何按季度分组统计_MySQL中按周、月、季、年分组统计
根据日期,按照周.月.季.年对数据进行分组统计 一.前言 带着问题去学习,我觉得是一种非常有效的学习方法,不知下面的几个问题是否也是你所需要考虑的:被分组的日期是否连续.周是以周日为起始日,还是以周一 ...
- MySQL基础——按月/周/日分组统计数据
MySQL基础--按月/周/日分组统计数据 在使用MySQL进行查询时,很多时候,我们需要按时间段进行统计,例如每周,每月的数据:这里我们需要使用到MySQL的关键词:DATE_FORMAT 1. 语 ...
- Mysql按日、周、月进行分组统计
我们在用Mysql抽取数据时候,经常需要按照天.周.月等不同的粒度对数据进行分组统计.而我们的时间可能是"2017/12/5 0:0:0"这种准确的时间.所以在进行分组之前我们需要 ...
- mysql怎么对月份进行统计_MySQL如何按月份统计数据详解(转)
这篇文章主要对MySQL按月份统计数据介绍:表finance有俩个字段如下date datemoney double(15,2)下面需要对表finance的2010年财务数据,按月进行统计 selec ...
- mysql获取当天每小时统计_详解mysql 获取某个时间段每一天、每一个小时的统计数据...
获取每一天的统计数据 做项目的时候需要统对项目日志做分析,其中有一个需求是获取某个给定的时间段内,每一天的日志数据,比如说要获取从2018-02-02 09:18:36到2018-03-05 23:1 ...
- MySQL按 年 月 周 日统计表中的数据
今天在做统计报表的时候,需要对表数据按年.月.周和日分别进行统计.统计用到了MySQL日期函数DATE_FORMAT.YEAR.QUARTER.MONTH和WEEK,本文就这些函数的使用和功能实现进行 ...
- mysql数据入库时间的统计_MySQL按时间统计数据的方法总结
在做数据库的统计时,经常会需要根据年.月.日来统计数据,然后配合echarts来制作可视化效果. 数据库:MySQL 思路 按照时间维度进行统计的前提是需要数据库中有保留时间信息,建议是使用MySQL ...
最新文章
- Python的XML-RPC学习
- java中的浮点计算
- 如何从Amazon API Gateway将查询字符串或路由参数传递到AWS Lambda
- Sublime Text 3 插件的安装、升级和卸载
- 超简易复制Model对象(为后续备忘录设计模式博文做铺垫)
- linux 文件解压 压缩
- iOS Hacker dumpdecrypted脱壳
- gradle 编译java_Java的Gradle依赖关系,使用编译还是实现?
- html2canvas图片位移_html2canvas html截图插件图片放大清晰度处理方案,支撑恣意放大倍数,处理原插件图片偏移题目...
- 经典面试题(42):以下代码将输出的结果是什么?
- matlab磁铁模拟,用matlab-模拟环形磁铁的磁场分布
- 第35次Scrum会议(11/23)【欢迎来怼】
- c语言中tail和head的用法,tail 与head用法
- 阿里巴巴CTO程立:践行“好科技”,我们正在做这些事
- 中国省份城市0-N编号
- 谈谈“天轰穿”老师对VS2005的视频讲座
- NVT平台model sensor配置
- 基于matlab的控制系统仿真题,MATLAB与控制系统仿期末考试试卷真
- 微信小程序连接mysql
- 【Python二级等考大题】星座三问
热门文章
- sqlhelper类 java_SQLHelper类(Java版)
- Abaqus 三维多面体骨料 随机多面体3D 无干涉多面体骨料模型
- html5小圆点,HTML5的Canvas实现小圆点在屏幕内跑动
- 阿里P6+的Java架构班课程招生,仅限20名!
- 华为手机什么时候更新鸿蒙系统_华为手机升级鸿蒙系统时间确认!这3部华为手机的机会最大...
- REVV Motorsport:SHRD 即将到来
- 怎么调图片分辨率?怎么改图片分辨率?
- Kali Linux渗透测试——MSF实战篇(一)
- 返回列表结果数据再请求详情
- 牛批!简单几步,无坑部署最小化 K8S 集群