随笔 - 47  文章 - 15  trackbacks - 0
<2019年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

因为口渴,上帝创造了水;
因为黑暗,上帝创造了火;
因为我需要朋友,所以上帝让你来到我身边
Click for Shaanxi xi'an, Shaanxi Forecast
╱◥█◣
  |田|田|
╬╬╬╬╬╬╬╬╬╬╬
If only I have such a house!
〖总在爬山 所以艰辛〗
Email:myesjoy@yahoo.com.cn
NickName:yesjoy
MSN:myesjoy@hotmail.com
QQ:150230516

〖总在寻梦 所以苦痛〗

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

文章档案

Hibernate在线

Java友情

Java认证

linux经典

OA系统

Spring在线

Structs在线

专家专栏

企业信息化

大型设备共享系统

工作流

工作流产品

网上购书

搜索

  •  

最新评论

阅读排行榜

评论排行榜

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★ 阅读(446) 评论(0)  编辑  收藏 所属分类: 数据库设计

只有注册用户登录后才能发表评论。


网站导航: