在Oracle中减去日期 - 数字或间隔数据类型?

时间:2012-02-17 04:32:21

标签: sql oracle date plsql oracle11g

我对Oracle DATE和INTERVAL数据类型的一些内部工作方式有疑问。根据{{​​3}},当您减去2个DATE数据类型时,结果将是NUMBER数据类型。

粗略测试,这似乎是真的:

CREATE TABLE test (start_date DATE);
INSERT INTO test (start_date) VALUES (date'2004-08-08');
SELECT (SYSDATE - start_date) from test;

将返回NUMBER数据类型。

但现在如果你这样做:

SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;

你得到一个INTERVAL数据类型。换句话说,Oracle可以将DATE减法中的NUMBER转换为INTERVAL类型。

所以现在我想我可以尝试直接在括号中输入NUMBER数据类型(而不是做'SYSDATE - start_date',这会产生一个NUMBER):

SELECT (1242.12423) DAY(5) TO SECOND from test;

但这会导致错误:

ORA-30083: syntax error was found in interval value expression

所以我的问题是:这里发生了什么?似乎减去日期应该导致NUMBER(如SELECT语句#1中所示),它不能自动转换为INTERVAL类型(如SELECT语句#3中所示)。但是,如果您使用DATE减法表达式而不是放入原始NUMBER(SELECT语句#2),Oracle似乎能够以某种方式这样做。

由于

5 个答案:

答案 0 :(得分:30)

好的,我通常不回答我自己的问题,但经过一些修补,我明确地想出了Oracle如何存储DATE减法的结果。

当你减去2个日期时,该值不是NUMBER数据类型(因为你会相信Oracle 11.2 SQL Reference manual)。 DATE减法的内部数据类型编号为14,这是一种未记录的内部数据类型(NUMBER为internal datatype number 2)。但是,它实际上存储为2个单独的二进制补码有符号数字,前4个字节用于表示天数,最后4个字节用于表示秒数。

DATE减法的示例,导致正整数差异:

select date '2009-08-07' - date '2008-08-08' from dual;

结果:

DATE'2009-08-07'-DATE'2008-08-08'
---------------------------------
                              364

select dump(date '2009-08-07' - date '2008-08-08') from dual;

DUMP(DATE'2009-08-07'-DATE'2008
-------------------------------
Typ=14 Len=8: 108,1,0,0,0,0,0,0

回想一下,结果表示为2个单独的2的补码4字节数字。由于在这种情况下没有小数(完全是364天和0小时),最后4个字节都是0,可以忽略。对于前4个字节,因为我的CPU具有little-endian架构,所以字节相反,应该读为1,108或0x16c,即十进制364。

DATE减法的示例,导致负整数差异:

select date '1000-08-07' - date '2008-08-08' from dual;

结果:

DATE'1000-08-07'-DATE'2008-08-08'
---------------------------------
                          -368160

select dump(date '1000-08-07' - date '2008-08-08') from dual;

DUMP(DATE'1000-08-07'-DATE'2008-08-0
------------------------------------
Typ=14 Len=8: 224,97,250,255,0,0,0,0

同样,因为我使用的是小端机器,所以字节相反,应该读作255,250,97,224,相当于11111111 11111010 01100001 11011111.现在因为这是二进制补码二进制数字编码,我们知道这个数字是负数,因为最左边的二进制数字是1.要将其转换为十进制数,我们必须反转2的补码(减去1然后做一个补码),结果为:00000000 00000101 10011110 00100000等于-368160被怀疑

导致小数差异的DATE减法示例:

select to_date('08/AUG/2004 14:00:00', 'DD/MON/YYYY HH24:MI:SS'
 - to_date('08/AUG/2004 8:00:00', 'DD/MON/YYYY HH24:MI:SS') from dual;

TO_DATE('08/AUG/200414:00:00','DD/MON/YYYYHH24:MI:SS')-TO_DATE('08/AUG/20048:00:
--------------------------------------------------------------------------------
                                                                             .25

这两个日期之间的差异是0.25天或6小时。

select dump(to_date('08/AUG/2004 14:00:00', 'DD/MON/YYYY HH24:MI:SS')
 - to_date('08/AUG/2004 8:00:00', 'DD/MON/YYYY HH24:MI:SS')) from dual;

DUMP(TO_DATE('08/AUG/200414:00:
-------------------------------
Typ=14 Len=8: 0,0,0,0,96,84,0,0

现在这次,因为差异是0天和6小时,所以预计前4个字节是0.对于最后4个字节,我们可以反转它们(因为CPU是小端)并得到84, 96 = 01010100 01100000 base 2 = 21600十进制。将21600秒转换为小时为您提供6小时,这是我们预期的差异。

希望这有助于任何想知道如何实际存储DATE减法的人。

答案 1 :(得分:6)

您收到语法错误,因为日期数学不会返回NUMBER,但会返回INTERVAL:

SQL> SELECT DUMP(SYSDATE - start_date) from test;

DUMP(SYSDATE-START_DATE)
-------------------------------------- 
Typ=14 Len=8: 188,10,0,0,223,65,1,0

您需要先使用NUMTODSINTERVAL Function

将示例中的数字转换为INTERVAL

例如:

SQL> SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;

(SYSDATE-START_DATE)DAY(5)TOSECOND
----------------------------------
+02748 22:50:04.000000

SQL> SELECT (SYSDATE - start_date) from test;

(SYSDATE-START_DATE)
--------------------
           2748.9515

SQL> select NUMTODSINTERVAL(2748.9515, 'day') from dual;

NUMTODSINTERVAL(2748.9515,'DAY')
--------------------------------
+000002748 22:50:09.600000000

SQL>

基于使用NUMTODSINTERVAL()函数的反向投射,在翻译中会出现一些舍入丢失。

答案 2 :(得分:1)

几点:

  • 从另一个日期中减去一个日期会产生一个数字;从另一个时间戳中减去一个时间戳会导致间隔。

  • Oracle在执行时间戳算术时将时间戳内部转换为日期。

  • 间隔常量不能用于日期或时间戳算法。

Oracle 11gR2 SQL Reference Datetime Matrix

答案 3 :(得分:0)

使用extract()函数从间隔值中检索小时/分钟/秒。参见下面的示例,如何从两个时间戳列中获取小时数。希望这会有所帮助!

从IHB_ADJSMT_WKFL_NTFCTN中提取IHB_INS_TS,MAIL_SENT_TS,从(IHB_INS_TS-MAIL_SENT_TS)中提取小时小时;

答案 4 :(得分:-3)

选择TIMEDIFF(STR_TO_DATE(' 07:15 PM ','%h:%i%p'),STR_TO_DATE('上午9:58 ',' %h:%i%p'))