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