数据库的日期类型字段该如何选择?

本文探讨了在需要兼容多种数据库产品的场景下,如何设计日期类型的存储方案。对比了MySQL、Oracle及SQL Server等数据库中日期类型的特点,并提出了使用毫秒数和包含时区的字符串格式作为解决方案。
当设计一个产品,其中很多地方要把日期类型保存到数据库中,如果产品有兼容不同数据库产品的需求,那么,应当怎样设计呢?
 
(1)
当然,首先想到的是,使用数据库的Date或DateTime类型,可是看看不同数据库这些类型间的区别吧,真让人望而止步。

mysql数据库:它们分别是 date、datetime、time、timestamp和year。
date :“yyyy-mm-dd”格式表示的日期值 
time :“hh:mm:ss”格式表示的时间值 
datetime: “yyyy-mm-dd hh:mm:ss”格式 
timestamp: “yyyymmddhhmmss”格式表示的时间戳值 
year: “yyyy”格式的年份值。

date “1000-01-01”到“9999-12-31” 3字节 
time “-838:59:59”到“838:59:59” 3字节 
datetime “1000-01-01 00:00:00” 到“9999-12-31 23:59:59” 8字节 
timestamp 19700101000000 到2037 年的某个时刻 4字节 
year 1901 到2155 1字节 




oracle数据库:
Date类型的内部编码为12 
长度:占用7个字节 
数据存储的每一位到第七位分别为:世纪,年,月,日,时,分,秒
TIMESTAMP是支持小数秒和时区的日期/时间类型。对秒的精确度更高
TIMESTAMP WITH TIME ZONE类型是TIMESTAMP的子类型,增加了时区支持,占用13字节的存储空间,最后两位用于保存时区信息
INTERVAL 用于表示一段时间或一个时间间隔的方法.在前面有多次提过.INTERVAL有两种类型. 
YEAR TO MONTH 能存储年或月指定的一个时间段. 
DATE TO SECOND存储天,小时,分钟,秒指定的时间段.


sql server:datetime和smalldatetime
datetime数据类型所占用的存储空间为8个字节,其中前4个字节用于存储1900年1月1日以前或以后的天数,数值分正负,正数表示在此日期之后的日期,负数表示在此日期之前的日期;后4个字节用于存储从此日零时起所指定的时间经过的毫秒数。
smalldatetime数据类型使用4个字节存储数据。其中前2个字节存储从基础日期1900年1月1日以来的天数,后两个字节存储此日零时起所指定的时间经过的分钟数。
smalldatetime数据类型与datetime数据类型相似,但其日期时间范围较小,从1900年1月1日到2079年6月6日。此数据类型精度较低,只能精确到分钟,其分钟个位为根据秒数四舍五入的值,即以30秒为界四舍五入。


     如果没有兼容多种数据库这个要求,我会毫不犹豫的使用数据库的Date类型。因为如果使用Java框架产生代码,对数据库中定义为Date类型的字段,甚至能在页面上产生出JS的时间选择框,的确能节省很多开发时间。
     而兼容不同数据库,就希望产品在由一种数据库,迁移到另外一种数据库时,尽可能小的代价,使用了Date,看来就很困难了。
    有一个疑问,不知道目前流行的ORM对这个处理得是不是好?因为工作不怎么涉及这方面,所以不大了解。
 
 
     在之前的设计开发中,因为有支持多种数据库这种需求,所以首先否定了日期时间这样的类型。
 
(2)
     曾经使用过毫秒数(java的 System.currentTimeMillis())这种方式,但是选用这个方式,考虑的不是使用起来是否方便或者数据迁移,而是考虑到下面的原因:
 
     java取到的毫秒数是对时间点的一种准确描述。定义如下:
 
      java.lang.System.currentTimeMillis(),
     它返回从 UTC 1970 年 1 月 1 日午夜开始经过的毫秒数。
 
     我们可以看到,这个定义,保证了这个时间值能够被后续设计开发的人员正确和准确的理解,能够为所有的应用正确理解,能够在所有时区上正确反映为正常的时间形式。
 
     当时的产品设计是有海外客户的,所以当时的设计,在数据库里保存的,应该是一个“准确的时间”。例如“20120926080000”实际上并没有严格的表示出时间,因为北京时间2012年9月26日8点和格林威治时间2012年9月26日8点显然是不一样的。
     虽然我们都是在一个确切的时区里,例如中国都是使用东八区时间,但是需要考虑的是:
(一)有些产品是可能有海外客户的
(二)产品所运行的机器,时区的设置未必都是东八区。
 
在这种情况下,如果数据库里的时间不准确,会给程序运行带来问题。
这种方式最大的缺点在于:
  •    不方便对时间进行分组查询,比如按月统计、按季 统计
  •     DBA在维护时,不能直观的根据返回的行结果,看到简单明了的结果(看到的是毫秒数)
 
使用这种方式的特点是牺牲一点易用性和可理解性(不易于维护和理解),满足了查询结果的直观性和准确性要求,同时最大限度考虑运行效率。
     为了解决这个问题,我设计了一个辅助的措施,就是建立一个数据库函数来进行时间转换,把毫秒数的时间转为制定时区和格式的时间串,DBA在维护时可以使用。测试了Oracle和DB2上,都可以这样。
    例如之前的查询的时候为:
 
      SELECT username,user_addtime from userinfo
       这个查询显示的是毫秒数
       使用内置函数后写成:
         SELECT username,date2str(user_addtime) from userinfo
    这样返回的就是东八区、预先定义好格式的字符串了。
(3)
在之后的设计里,还使用过YYYYMMDDHHmmSST格式,其中的“T”指时区,加入时区,带来的影响有:
1)日期时间字段就不能在使用数值来存储了,字符串比数字存储和检索的效率都要低。
2)应用程序需要加上额外的处理
带来的好处是:
1)便于DBA维护
2)到什么时候,即便没有看到数据库设计文档,都能看明白并准确理解数据库中一条信息中,这个字段保存到确切信息 
使用这种方式的特点是牺牲一点效率,满足了查询结果的直观性和准确性要求。
总结一下,字段类型的选择,还是根据场景的需要来选择,从功能、效率要求、持续开发的要求、维护的要求几个方面综合考虑。


<think>我们正在讨论如何在Oracle数据库的表中添加日期类型字段。 根据之前的对话,用户已经了解了添加字段的基本语法,现在特别询问如何添加日期类型字段。 引用[2]和[4]提供了相关线索:引用[2]展示了创建表时包含日期字段的示例,引用[4]展示了插入日期数据时使用to_date函数转换。 但用户当前问题聚焦于“添加日期类型字段”,而非创建表或插入数据。 因此,我们将结合之前回答的添加字段的基本语法,并特别指定数据类为DATE。 步骤: 1. 使用ALTER TABLE语句添加字段,指定数据类为DATE。 2. 可选:可以为该字段添加约束(例如NOT NULL)或默认(例如DEFAULT SYSDATE)。 示例: 添加一个简单的日期字段: ALTER TABLE employees ADD (birth_date DATE); 添加一个非空且有默认日期字段(注意:对于现有表添加非空字段,必须指定默认,否则会报错): ALTER TABLE employees ADD (created_date DATE DEFAULT SYSDATE NOT NULL); 注意:在添加字段时,如果指定了默认,那么表中已有记录的这个字段将被设置为默认。对于新插入的记录,如果不指定该字段,则自动使用默认。 同时,引用[3]提到在Java中处理日期字符串存入Oracle日期字段的方法,但我们当前的问题主要是添加字段,所以仅作为扩展参考。 根据引用[4]可知,插入日期数据时通常使用to_date函数,但添加字段本身不需要这个函数。 因此,回答重点放在添加日期字段的SQL语句上。 同时,生成相关问题。 </think>### 如何在Oracle数据库中添加日期类型字段 在Oracle中为表添加日期类型字段需使用 `ALTER TABLE...ADD` 语句,核心语法如下: ```sql ALTER TABLE 表名 ADD ( 字段名 DATE [DEFAULT 默认] [NOT NULL] ); ``` #### 操作步骤详解 1. **基本日期字段添加** ```sql -- 添加基础日期字段(允许NULL) ALTER TABLE employees ADD hire_date DATE; ``` 2. **带默认日期字段** ```sql -- 添加默认为当前系统时间的字段 ALTER TABLE employees ADD created_time DATE DEFAULT SYSDATE; ``` 3. **非空日期字段** ```sql -- 添加非空日期字段(必须同时指定DEFAULT) ALTER TABLE employees ADD review_date DATE DEFAULT SYSDATE NOT NULL; ``` #### 关键注意事项 1. **默认规则**: - `DEFAULT` 仅对新插入记录生效 - 已有记录的日期字段默认为 `NULL`(除非显式更新) - 常用默认函数: ```sql SYSDATE -- 当前系统时间 TRUNC(SYSDATE) -- 今天零点 SYSDATE - 7 -- 7天前 ``` 2. **数据插入技巧**: 插入时需用 `TO_DATE()` 转换字符串(引用[4]): ```sql INSERT INTO employees (hire_date) VALUES (TO_DATE('2023-08-15', 'YYYY-MM-DD')); ``` 3. **字段验证方法**: ```sql SELECT column_name, data_type, data_default FROM user_tab_columns WHERE table_name = 'EMPLOYEES' AND column_name = 'HIRE_DATE'; ``` 输出示例: ``` COLUMN_NAME | DATA_TYPE | DATA_DEFAULT -------------------------------------- HIRE_DATE | DATE | (null) ``` > 💡 **设计建议**:业务中需要频繁处理周/月汇总时(引用[1]),优先考虑添加日期字段而非字符串类字段,可显著简化日期计算查询[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值