在Oracle中,Date和TimeStamp類型存儲精度可變的固定日期時間。Intreval可以很容易地存儲一個時間量,如“8個小時”或“30天”,將兩個時間戳相減,就會得到一個時間間隔(Interval),例如,將8小時加到一個TimeStamp上,即得到一個新的TimeStamp。
Oracle 5中已經支持Date類型,TimeStamp和Interval在Oracle 9i Release 1中才被引入。因此,Date最為常見,但你應該在新應用中使用TimeStamp類型,因為:
1、TimeStamp支持小數秒,而Date不支持;
2、TimeStamp支持時區,Date不支持。
(一)Date類型
Date類型是一個7字節的定寬日期/時間數據類型,它包含7個屬性:世紀、世紀中哪一年、月份、月中的哪一天、小時、分鍾、秒。Oracle使用一種內部格式來表示,它並不是保存20,12,10,31,12,01,00來表示2012年10月31日12:01:00,這個可以通過內置函數DUMP來查看:
Create table t (x date); insert into t(x) values (to_date('2012-11-24 12:01:00','YYYY-MM-dd HH24:mi:ss')); select x,dump(x,10) d from t;
查詢結果:
世紀和年份字節,即(120,112)采用一種“加100”的表示法來存儲。必須將其減去100才能得到正確的世紀和年份。這是為了支持BC和AD日期。
如果從世紀字節減100得到負數,表示BC日期,例如:
insert into t (x) values( to_date( '01-08-4712bc','dd-MM-yyyybc hh24:mi:ss' ) );
此時,會報一個錯,“ORA-01856: 要求BC/B.C. 或AD/A.D.”。
修改為如下語句:
Insert into t (x) values(to_date('01-08-4712BC','dd-MM-YYYYBC','nls_date_language=American')); insert into t (x) values( to_date( '01-08-4710BC 12:01:13','dd-MM-yyyyBC hh24:mi:ss','nls_date_language=American') );
查看結果:
別擔心,讀出的世紀和年份是“虛”的,其實記錄是正確的,以下語句即可。
select x, to_char(x,'dd-mm-yyyybc','nls_date_language=American') AS TrueDate,dump(x,10) d from t;
世紀字節是53,而53-100=-47,這才是我們插入的真實世紀。由於這是一個負數,我們知道它是一個BC日期。這種存儲格式還允許日期以一種二進制方式自然地排序。由於4712BC小於4710BC,我們可以測試一下排序:
select x, to_char(x,'dd-mm-yyyybc','nls_date_language=American') AS TrueDate,dump(x,10) d from t order by TRUEDATE ASC;
接下來月份和日字節,自然存儲,不作任何修改。小時、分、秒采用“加1”表示法存儲,也就是各部分都減1,才能得到真實的時間。因此,午夜0點對應的日期字段表示為1,1,1
這種7字節格式能自然排序,可以采用一種進二進制方式按從小到大或從大到小的順序非常高效地進行排序。另外,這種結構允許很容易地進行截斷,而無需把日期變成其他格式。即將尾部三個字節設置為1,1,1即可清除時間分量。
創建一個新表:
create table t ( what varchar2(10), x date ); insert into t (what, x) values ( 'downmoon', to_date( '25-11-2012 22:01:00', 'dd-MM-yyyy hh24:mi:ss' ) ); insert into t (what, x) select 'minute', trunc(x,'mi') from t union all select 'day', trunc(x,'dd') from t union all select 'month', trunc(x,'mm') from t union all select 'year', trunc(x,'y') from t / select what, x, dump(x,10) d from t;
結果:
要把這個日期截斷,只取年份,數據庫只要后5個字節置1,這是一個非常快速的操作。
而要向一個date增加時間或減去時間,請先遵循以下原則:
(1)使用NUMTODSINTERVAL增加時分秒;
(2)使用Date直接加數字增加天;
(3)使用ADD_MONTHS內置函數增加月和年;不建議使用NUMTOYMINTERVAL函數。因為前者會幫我們完成日期的舍入。
看示例:
alter session set nls_date_format='dd-MM-yyyy hh24:mi:ss'; select dt,add_months(dt,1) dt1,dt+NUMTOYMINTERVAL(1,'month') DT2 from (select to_date('29-02-2012','dd-MM-yyyy')dt from dual);
注意:ADD_MONTHS是添加自然月,而NUMTOYMINTERVAL是加到下個月的同一天,不管這一天是否有效,如果3月31日使用NUMTOYMINTERVAL加一個月,系統會報錯。
因此,這句會正確執行:
select dt,add_months(dt,1) dt1 from (select to_date('31-03-2012','dd-MM-yyyy')dt from dual);
而這句會報錯:指定月份的日期無效。
同理,向2012年2月29日增加1 年也會報錯,因為不存在2013年2月29日。
另外一個應用就是如何得到兩個日期之差,方法如下:
select numtoyminterval (trunc(months_between(dt2,dt1)),'month') years_months, numtodsinterval (dt2-add_months( dt1, trunc(months_between(dt2,dt1)) ), 'day' ) days_hours from (select to_date('2012-11-29 01:02:03','yyyy-MM-dd hh24:mi:ss') dt1, to_date('2014-03-15 11:22:33','yyyy-MM-dd hh24:mi:ss') dt2 from dual );
這個結果清楚地表明了兩個日期相差1年、3個月、15天、10小時、20分、30秒。
下面一節我們將了解了下TimeStamp