項目中的統計報表作的很多,需求中有按周、月統計數據的。查看了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 |
年,4 位 |
%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 |
年,其中的星期日是周的第一天,4 位,與 %V使用 |
%x |
年,其中的星期一是周的第一天,4 位,與 %v使用 |
%Y |
年,4 位 |
%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()的語法,看後快暈過去了,這麼低級的錯誤我怎麼之前一直沒有發現呢?
%M |
月名 |
%m |
月,數值(00-1 |