我对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似乎能够以某种方式这样做。
由于
答案 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在执行时间戳算术时将时间戳内部转换为日期。
间隔常量不能用于日期或时间戳算法。
答案 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'))