4、在數(shù)據(jù)統(tǒng)計(jì)上的比較
假設(shè)要根據(jù)EXPIRE_DATE字段,統(tǒng)計(jì)2005年每月到期的用戶數(shù)。CHAR類型日期和DATE類型日期對(duì)應(yīng)的統(tǒng)計(jì)SQL語句分別分析如下。
·CHAR類型日期
這種日期的表,要在日期字段上做統(tǒng)計(jì)是很方便的,下面的SQL語句統(tǒng)計(jì)出2005每月的T_USER記錄:
select substr(EXPIRE_DATE,5,2) MONTH,count(USER_ID) from T_USER where EXPIRE_DATE like '2005%'group by substr(EXPIRE_DATE,5,2) |
表 5 CHAR類型日期統(tǒng)計(jì)SQL
其中substr(EXPIRE_DATE,5,2)字符串處理函數(shù)獲取日期的月份,如"20060102"通過該函數(shù)即得到"01"的月份值。而"EXPIRE_DATE like '2005%'"的條件式即過濾出所有2005年的數(shù)據(jù),并且可以使用EXPIRE_DATE字段上的索引。
·DATE類型日期
對(duì)于DATE類型日期按月進(jìn)行統(tǒng)計(jì),乍一看,可以采用和CHAR類型日期相似的統(tǒng)計(jì)SQL語句:
select extract(month from EXPIRE_DATE) MONTH,count(USER_ID) from T_USER where extract(year from EXPIRE_DATE ) = 2005 group by extract(month from EXPIRE_DATE) |
表 6 DATE類型日期統(tǒng)計(jì)SQL(全表掃描)
注:在
Oracle中通過extract(<日期域名> from Date)函數(shù)獲取日期的某特定日期域仁值。
但是仔細(xì)一分析,就會(huì)發(fā)現(xiàn)由于where條件式中對(duì)索引字段EXPIRE_DATE使用了extract()函數(shù),因此EXPIRE_DATE上的索引在此統(tǒng)計(jì)SQL中將無法使用,所以該統(tǒng)計(jì)將引發(fā)一個(gè)全表掃描。
當(dāng)然,你可以在EXPIRE_DATE字段上建立函數(shù)索引,但EXPIRE_DATE上的查詢可以不僅僅只會(huì)用到extract()函數(shù),一一為這些EXPIRE_DATE字段建立多個(gè)函數(shù)索引不但麻煩而且會(huì)影響T_USER上數(shù)據(jù)更新操作的性能。
當(dāng)然,除了上式的統(tǒng)計(jì)方法外,還可以采用另外一種方法:
select extract(month from EXPIRE_DATE) month,count(USER_ID) from T_USER where EXPIRE_DATE between to_date('20050101','yyyymmdd') and to_date('20051231','yyyymmdd') group by extract(month from EXPIRE_DATE) |
表 7 DATE類型日期統(tǒng)計(jì)SQL(使用索引)
表 7的方法可以使用EXPIRE_DATE字段上的索引,但又引入了一個(gè)我們前面已經(jīng)提到過的不可避免的問題:必須按日期語義構(gòu)造出開始和結(jié)束日期,以形成一個(gè)日期區(qū)間。在該例中,由于是對(duì)一整年進(jìn)行統(tǒng)計(jì),因而開始結(jié)束日期容易獲得,如果是精確到月的日期區(qū)間,則需要計(jì)算出某月的最后一天,程序就復(fù)雜多了。但如果是CHAR類型日期的表,構(gòu)造統(tǒng)計(jì)SQL語句,也易如反掌:如我們要統(tǒng)計(jì)200304~200402每月到期的用戶數(shù),可以通過以下SQL語句:
select substr(EXPIRE_DATE,1,6) year_month ,count(USER_ID)from T_USER where EXPIRE_DATE between '20030400' and '20040299'group by substr(EXPIRE_DATE,1,6) |
即將開始日期以0補(bǔ)齊到8位,將結(jié)束日期以9補(bǔ)齊到8位。
5、在數(shù)據(jù)庫移植上的比較
由于CHAR類型日期實(shí)際上是一個(gè)字符類型字段,字符類型是最基本的數(shù)據(jù)類型,在構(gòu)造Insert ,Update,Delete,Selete的SQL時(shí),各種數(shù)據(jù)庫對(duì)字符類型字段的處理幾乎一致,因此在數(shù)據(jù)庫的移植上比較容易。
對(duì)于DATE類型的日期,由于不同數(shù)據(jù)庫對(duì)日期的操作差異很大,如獲取數(shù)據(jù)庫的時(shí)間函數(shù),Oracle為sysdate,SqlServer為getdate(),而MySql為now();從Date字段中抽取年的數(shù)值,Oracle為extract(year from ),SqlServer和MySql均為month()。由于日期函數(shù)在不同數(shù)據(jù)庫差別巨大,帶DATE類型日期字段的表在數(shù)據(jù)庫的移植上就不如CHAR類型日期來得簡單易行。
也許,有人會(huì)說現(xiàn)在都采用Hibernate進(jìn)行映射ORM了,Hibernate已經(jīng)屏蔽了具體數(shù)據(jù)庫的不同,何來的數(shù)據(jù)庫移植?這話在一定程度上是沒有錯(cuò)的,但是Hibernate框架由于通過對(duì)象映射的方法產(chǎn)生SQL語句,有時(shí)往往很難獲得最優(yōu)的查詢性能的SQL語句。所以,對(duì)于一些有性能要求較高的查詢,往往采用直接編寫SQL語句,或采用iBatis框架,后兩者都需要直接使用SQL語句,此時(shí)數(shù)據(jù)庫的移植問題就暴露出來了。
不但在數(shù)據(jù)庫的移植問題上,CHAR類型日期比DATE類型日期擁有絕對(duì)的優(yōu)勢(shì),在數(shù)據(jù)的導(dǎo)入/導(dǎo)出,數(shù)據(jù)傳輸?shù)确矫妫珻HAR類型日期比DATE類型日期也具有較多的優(yōu)勢(shì)。字符型的數(shù)據(jù)可以直接不失真地用文本或XML表示,而Date類型導(dǎo)出為文本時(shí),如果不指定好轉(zhuǎn)換格式往往難于處理,如2001-01-01的Date數(shù)據(jù)在轉(zhuǎn)換為文本時(shí),可能變?yōu)?st Mon 2001,也可能為2001-01-01 00:00:00 ,甚至可能是01-01-01。這樣,在導(dǎo)入時(shí)顯得難以操作,因?yàn)閷?dǎo)入/導(dǎo)出都需要指定好日期格式。
6、總結(jié)
有一句很經(jīng)典的關(guān)于軟件設(shè)計(jì)的話:如果你的程序邏輯變得很復(fù)雜,也許并不是問題域本身的復(fù)雜度造成的,往往將歸因于設(shè)計(jì)上的缺陷和瑕疵。同樣一個(gè)問題,采用不同的策略,往往造成大相徑庭的解決復(fù)雜度。Spring框架功能強(qiáng)大,我本以為代碼一定很復(fù)雜,但是當(dāng)我研讀了Spring框架的代碼時(shí),才詫異地發(fā)現(xiàn)Spring框架的源碼很少有超過300行代碼的類,類和方法大多簡潔明了,真是應(yīng)了那句大巧若拙,大道至簡的話了。
在庫表設(shè)計(jì)時(shí),日期字段究竟是采用CHAR類型日期還是DATE類型日期,在作出選擇時(shí),需要考慮在程序邏輯中該數(shù)據(jù)的加工操作邏輯,畢竟表字段是需要在程序邏輯中使用和操作的,而非僅僅做一個(gè)簡單的記錄而已。
通過上文的分析,我們發(fā)現(xiàn)CHAR類型日期可以在較大的程度上簡化程序的開發(fā),并且充分利用索引,獲取較好的性能。但又一個(gè)問題產(chǎn)生了,既然CHAR類型日期這么不好用,各數(shù)據(jù)庫又都提供了Date日期格式,是否Date數(shù)據(jù)類型就成了尸位素餐的擺設(shè)了呢?換言之,Date數(shù)據(jù)類型適合在什么場合使用呢?筆者個(gè)人的建議是,在幾乎任何時(shí)候都不要使用Date類型作為表字段類型,Date類型僅在存儲(chǔ)過程,數(shù)據(jù)庫函數(shù)這些數(shù)據(jù)庫程序邏輯代碼編寫場合使用,即把它看成是一個(gè)運(yùn)算過程的中間工具而不要用其作數(shù)據(jù)的存儲(chǔ)形式。
另外還有一個(gè)需要探討的問題,那就是Date類型長度是7,而Char(8)或Char(14)要比之浪費(fèi)不少的存儲(chǔ)空間,其中這個(gè)問題在古代確實(shí)是一個(gè)大問題,那時(shí)候一間茅屋都要合理利用,現(xiàn)在很容易就可以得到廣廈千萬間。由于硬件性價(jià)比持續(xù)提升,也就可以使我們采用一些軟件上更簡便的方法來改善程序的設(shè)計(jì),如Java的代碼反射,IoC的實(shí)現(xiàn)注入,XML形式的數(shù)據(jù)表示都是受惠于硬件的提升。因此,現(xiàn)在,一般而言,很微小的存儲(chǔ)空間占用和性能的影響并不需要設(shè)計(jì)人員特別的關(guān)注,他們要更多關(guān)注的往往是如何使邏輯簡化,如何使系統(tǒng)更具擴(kuò)展性,可維護(hù)性和移植性上