Mysql 中使用DATE_FORMAT函数按月、周统计数据

本文深入探讨MySQL中的Date_format函数在按周、月统计数据时的特殊用法及注意事项,特别是关于年份表示的不同格式(%X与%Y)可能导致的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 #按天统计
  SELECT date,SUM(macs) AS macsAll FROM tb 
  WHERE DATE_FORMAT(date,'%Y-%m-%d')<'2014-03-01' GROUP BY date
  #按周统计
  SELECT DATE_FORMAT(date,'%x-%v') AS date,SUM(macs) AS macs 
  FROM tb 
  GROUP BY date 
  #按月统计
  SELECT SUM(macs) AS macs,MONTH(date) AS date,names FROM tb  
  WHERE names='上海' GROUP BY date; 


项目中的统计报表作的很多,需求中有按周、月统计数据的。查看了Mysql的API,发现Date_format是格式化日期的,看了Date_format()的具体说明后就用这个函数按周统计,sql大致如下:

select   DATE_FORMAT(check_date ,'%X-%V')  dates,avg(weight)/10000  weight from ho_body where user_id=295

 and  weight >0 and check_date  between '2009-02-24' and '2010-02-24' group by dates

------------------------------------------------------------------------------------------------------

查出的结果为:

dates       weight

2009-30   80.9
2009-31   80.425
2009-32   80.76666667
2009-33   80.75384615
2009-34   80.8
2009-35   79.88
2009-36   80.06
2009-37   79.875
2009-38   79.075
2009-39   79.26666667
2009-40   79.16666667
2009-41   78.875
2009-42   78.33333333
2009-43   78.77272727
2009-44   77.625
2009-45   77.825
2009-46   77.575
2009-47   77.45
2009-48   81.25
2009-49   76.5
2009-50   77.83333333
2009-52   79.8
2010-02   79.2

 

 

%X

年,其中的星期日是周的第一天,4 位,与 %V 使用

%x

年,其中的星期一是周的第一天,4 位,与 %v 使用

%Y

年,

%y

年,2 位

 

难道%X只能与%V一起用表示年-周,并且周日为一周开始,

%x与%v一起用,表示年-周,周一为一周开始?

再看到前面的

 

%V

 (01-53) 星期日是一周的第一天,与 %X 使用

%v

 (01-53) 星期一是一周的第一天,与 %x 使用

看来是这个意思了,我之前怎么就没有注意到这个特别之处呢?可能是当时作完以周统计,然后再写月统计时,一看m表示月,数值,就直接把Date_format('%X-%V')改为Date_format('%X-%m')了。再加上%Y年,4位,%m月,没有具体说明,所以一直没有发现,并且用%X-%m按月统计,测试了许多数据,发现年-月,数值全是正确的。结果在跨年的情况下有问题了。。。一般情况下我是会考虑跨年的情况的,比如在求一个日期属于这一年的第几周,在这个问题上,我测试2009-12-31,2010-01-01属于哪一周,2010-01-03是哪一周,发现之前的方法有bug,后来作了修正才正确。而这是用mysql函数统计,测试一些数据没问题就觉得ok了,根本没有想到这样写在跨年时有问题了。。。。

 

为了将这个问题彻底整理清楚,下面是我找到的详细解读:

Date_format可以使用的格式有:

 

格式

描述

%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

年,其中的星期日是周的第一天,位,与 %V 使用

%x

年,其中的星期一是周的第一天,位,与 %v 使用

%Y

年,

%y

年,2 位

 

Mysql中还有另外几种返回日期的函数,如:

SELECT  EXTRACT(YEAR_MONTH  FROM  datecolum ) 返回格式如200902

EXTRACT() 函数用于返回日期/时间的单独部分,比如年、月、日、小时、分钟等等。

语法

EXTRACT(unit FROM date)

date 参数是合法的日期表达式。unit 参数可以是下列的值:

 

Unit 值

MICROSECOND

SECOND

MINUTE

HOUR

DAY

WEEK

MONTH

QUARTER

YEAR

SECOND_MICROSECOND

MINUTE_MICROSECOND

MINUTE_SECOND

HOUR_MICROSECOND

HOUR_SECOND

HOUR_MINUTE

DAY_MICROSECOND

DAY_SECOND

DAY_MINUTE

DAY_HOUR

YEAR_MONTH

 

Unit 值

MICROSECOND

SECOND

MINUTE

HOUR

DAY

WEEK

MONTH

QUARTER

YEAR

SECOND_MICROSECOND

MINUTE_MICROSECOND

MINUTE_SECOND

HOUR_MICROSECOND

HOUR_SECOND

HOUR_MINUTE

DAY_MICROSECOND

DAY_SECOND

DAY_MINUTE

DAY_HOUR

YEAR_MONTH

 

检查了一下,大致没有问题,然后又开始寻找如何按月统计,然后就看Date_format的语法,

%m表示数值,%M表示名称,看后很兴奋的试一下:

select   distinct  DATE_FORMAT(check_date ,'%X-%m')  c1,avg(weight)/10000  wei from ho_body where user_id=295 
 and  weight >0 and check_date  between '2009-02-24' and '2009-10-31' group by c1

——————————————————————————————————————

c1               wei

2009-08    80.66388889
2009-09    79.70555556
2009-10    78.83714286

 

然后测试这样计算的结果是否正确,经过测试,果然是没问题的,很有成就感。然后就用这个方法统计,拿取数据,然后将这些统计的结果作为数据集,用JfreeChart画图。后期测试时,发现画的统计图横轴日期有问题,时间范围为2009-02-24~2010-02-24时,图片中竟然多出一个2009年1月,并且在最右侧。我确信我是经过排序的,不可能2009年1月会在最右侧,之后再调试,竟然发现数据库中根本就没有09年1月的数据,更直观的错误是,以between '2009-02-24' and '2009-10-31' 条件,根本不可能出现2009-01。

 

然后将错误原因定位在sql语句上,执行这个语句,果然,原因就在这里:

select   distinct  DATE_FORMAT(check_date ,'%X-%m')  c1,avg(weight)/10000  wei from ho_body where user_id=295 
 and  weight >0 and check_date  between '2009-02-24' and '2010-02-24' group by c1

————————————————————————————————————————

c1              wei

2009-01    79.8
2009-08    80.66388889
2009-09    79.70555556
2009-10    78.83714286
2009-11    77.64285714
2009-12    78.75
2010-01    79.2

这个09年1月到底是哪里出来的,我先确定2009年1月是否有数据,结果测试最早的数据为2009-08-01,并没有1月的任何数据。既然最早的数据是8月,那么就测试这个多出来的2009-01是从哪里冒出来的。

我就用最笨的方法测试:
select  distinct DATE_FORMAT(check_date ,'%X-%m')c1   from ho_body where user_id=295 and check_date  between '2009-08-01' and '2009-08-31' order by c1

————————————————————————————————————————

c1

2009-08

问题不在8月。继续测试:

select  distinct DATE_FORMAT(check_date ,'%X-%m')c1   from ho_body where user_id=295 and check_date  between '2009-9-01' and '2009-09-30' order by c1

——————————————————————————————————————

 

c1

2009-09

 

……

……

直到2010年时,


select  distinct DATE_FORMAT(check_date ,'%X-%m')c1   from ho_body where user_id=295 and check_date  between '2010-01-01' and '2010-01-31' order by c1

————————————————————————————————————————

c1

2009-01
2010-01

问题就是2010年1月,并且,如果将条件改为between '2010-01-02' and '2010-01-31' 则不会多出2009-01。难道是Date_format函数有bug,在跨年时没有考虑正确,将2010年1月1日归为2009年的1月中?当然我也有怀疑过是我的sql语句有问题,仔细再看了看Date_format()的语法,

%X表示年,周日为一周第一天

%x表示年,周一为一周第一天

%M表示月,名称

%m表示月,数值,01形式

%V表示周,周日为第一天

%v表示周,周一为第一天

...........

 

看来看去,感觉没什么错误呀,感觉自己对这个研究的够清楚明白了,一定没有问题的。

mysql有bug的想法我坚持了一下午,在我和一个同事说这个bug的时候,老大听到了,问我具体怎么回事,我就给她讲解,特别奇怪的地方在于数据库根本没有09年1月的数据,用mysql中的Date_format的按月统计函数时,在跨年时有问题。老大也觉得这个问题不可思议,这时那个同事说,你怎么用X表示年呀,一般不都是有Y表示年吗?我还反驳说,这个没区别的呀,都是一样的,表示年嘛,4位的。。。。

后来和他争论之中,为了表明修改这个X,Y什么的没效果,我就改成了%Y-%m,结果却是2009-01没有了!!!!

怎么会这样????也太低级了吧,之前一直怀疑mysql的bug,竟然是这个原因?然后再仔细看Date_format()的语法,看后快晕过去了,这么低级的错误我怎么之前一直没有发现呢?

<think>我们正在讨论的是MySQL中的DATE_FORMAT函数,而不是Greenplum。根据引用内容,DATE_FORMATMySQL中用于日期格式化的函数。用户的问题:如何查询MySQL数据库中date_format函数使用方式根据引用[1][2][3]:基本语法:DATE_FORMAT(date,format)参数:date:要格式化的日期或时间表达式format:指定输出格式的字符串,由特定的格式说明符组成常用格式说明符:%Y:四位年份(例如:2024)%y:两位年份(例如:24)%m:两位份(01到12)%c:份(1到12,不带前导零)%d:两位日期(01到31)%e:日期(1到31,不带前导零)%H:24小时制的小时(00-23)%h:12小时制的小时(01-12)%i:两位分钟(00-59)%s:两位秒(00-59)%p:AM或PM%W:星期几的全称(例如:Sunday)%a:星期几的缩写(例如:Sun)%M:份的全称(例如:January)%b:份的缩写(例如:Jan)示例(来自引用[3]):DATE_FORMAT(NOW(),'%b%d%Y%h:%i%p')->例如:Jan01202402:30PMDATE_FORMAT(NOW(),'%m-%d-%Y')->例如:01-01-2024DATE_FORMAT(NOW(),'%d %b %y')->例如:01Jan24DATE_FORMAT(NOW(),'%d%b%Y%T:%f')->例如:01 Jan202414:30:00.000000另外引用[2]中的例子:SELECTDATE_FORMAT(my_date,'%Y-%m-%d') ASformatted_dateFROM my_table;使用步骤:1.确定要格式化的日期列或日期值(可以是列名,也可以是NOW()等函数返回的当前时间)2.根据需要选择格式说明符组合成格式字符串注意事项:-格式字符串中,除了格式说明符以外的字符都会原样输出,因此可以添加分隔符(如-、/、空格)和文字(如“年”、“”)。但注意,如果要输出%字符,需要用两个百分号(%%)转义。-格式说明符区分大小写,例如%Y和%y不同,%M和%m不同。示例:包含文字SELECT DATE_FORMAT('2024-01-01', '%Y年%m%d日');->2024年0101日因此,回答如下:</think>### MySQL中`DATE_FORMAT()`函数使用详解 `DATE_FORMAT()`是MySQL中用于将日期/时间值格式化为指定字符串的核心函数,通过**格式化说明符**实现灵活的时间展示。以下是详细使用方法: #### 基础语法 ```sql DATE_FORMAT(date, format) ``` - **date**:要格式化的日期/时间列或表达式(如`NOW()`、`CURDATE()`或表字段) - **format**:指定输出格式的字符串,由**格式化说明符**和普通字符组成 #### 常用格式化说明符 | 说明符 | 含义 | 示例输出 | |--------|--------------|---------------| | `%Y` | 四位年份 | 2024 | | `%y` | 两位年份 | 24 | | `%m` | 份(01-12) | 07 | | `%c` | 份(1-12) | 7 | | `%d` | 日期(01-31) | 15 | | `%e` | 日期(1-31) | 15 | | `%H` | 24小时制小时 | 14 | | `%h` | 12小时制小时 | 02 | | `%i` | 分钟(00-59) | 30 | | `%s` | 秒(00-59) | 45 | | `%p` | AM/PM | PM | | `%W` | 星期全称 | Monday | | `%a` | 星期缩写 | Mon | | `%M` | 份全称 | July | | `%b` | 份缩写 | Jul | #### 实际应用示例 1. **基础日期格式化** ```sql -- 输出:2024-07-15 SELECT DATE_FORMAT(NOW(), '%Y-%m-%d'); ``` 2. **包含文字的复杂格式** ```sql -- 输出:2024年0715日 14:30:45 SELECT DATE_FORMAT(NOW(), '%Y年%m%d日 %H:%i:%s'); ``` 3. **按份聚合数据** ```sql -- 统计每订单量 SELECT DATE_FORMAT(order_date, '%Y-%m') AS month, COUNT(*) AS order_count FROM orders GROUP BY month; ``` 4. **12小时制时间显示** ```sql -- 输出:02:30 PM SELECT DATE_FORMAT('2024-07-15 14:30:45', '%h:%i %p'); ``` 5. **英文日期格式** ```sql -- 输出:Jul 15, 2024 SELECT DATE_FORMAT(NOW(), '%b %e, %Y'); ``` #### 注意事项 1. **普通字符处理**:非说明符字符(如`年`、`-`、`,`)直接输出 2. **大小写敏感**:`%M`(份全称)和`%m`(数字份)效果不同 3. **空值处理**:若`date`为`NULL`,结果返回`NULL` 4. **性能优化**:避免在`WHERE`条件中使用格式化函数,可能无法使用索引 > 示例参考:DATE_FORMAT(NOW(),'%b %d %Y %h:%i %p') 输出类似 "Jul 15 2024 02:30 PM"[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值