BYS*_*YS2 21 sql oracle plsql date oracle11g
我对Oracle DATE和INTERVAL数据类型的一些内部工作方式有疑问.根据Oracle 11.2 SQL Reference,当您减去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;
Run Code Online (Sandbox Code Playgroud)
将返回NUMBER数据类型.
但现在如果你这样做:
SELECT (SYSDATE - start_date) DAY(5) TO SECOND from test;
Run Code Online (Sandbox Code Playgroud)
你得到一个INTERVAL数据类型.换句话说,Oracle可以将NUMBER从DATE减法转换为INTERVAL类型.
所以现在我想我可以尝试直接在括号中输入NUMBER数据类型(而不是做'SYSDATE - start_date',这会导致NUMBER反正):
SELECT (1242.12423) DAY(5) TO SECOND from test;
Run Code Online (Sandbox Code Playgroud)
但这会导致错误:
ORA-30083: syntax error was found in interval value expression
Run Code Online (Sandbox Code Playgroud)
所以我的问题是:这里发生了什么?似乎减去日期应该导致NUMBER(如SELECT语句#1中所示),它不能自动转换为INTERVAL类型(如SELECT语句#3中所示).但是,如果使用DATE减法表达式而不是放入原始NUMBER(SELECT语句#2),Oracle似乎能够以某种方式做到这一点.
谢谢
BYS*_*YS2 34
好吧,我通常不回答我自己的问题,但经过一些修补,我明确地想出了Oracle如何存储DATE减法的结果.
当您减去2个日期时,该值不是NUMBER数据类型(因为Oracle 11.2 SQL Reference手册会让您相信).DATE减法的内部数据类型编号为14,这是一种未记录的内部数据类型(NUMBER是内部数据类型编号2).但是,它实际上存储为2个单独的二进制补码有符号数,前4个字节用于表示天数,最后4个字节用于表示秒数.
DATE减法的示例导致正整数差异:
select date '2009-08-07' - date '2008-08-08' from dual;
Run Code Online (Sandbox Code Playgroud)
结果是:
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
Run Code Online (Sandbox Code Playgroud)
回想一下,结果表示为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;
Run Code Online (Sandbox Code Playgroud)
结果是:
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
Run Code Online (Sandbox Code Playgroud)
再次,由于我使用的是小端机器,字节相反,应该读取为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
Run Code Online (Sandbox Code Playgroud)
这两个日期之间的差异是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
Run Code Online (Sandbox Code Playgroud)
这一次,由于差异是0天和6小时,预计前4个字节为0.对于最后4个字节,我们可以反转它们(因为CPU是小端)并得到84,96 = 01010100 01100000 base 2 = 21600十进制.将21600秒转换为小时为您提供6小时,这是我们预期的差异.
希望这有助于任何想知道如何实际存储DATE减法的人.
您得到语法错误,因为日期数学不返回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
Run Code Online (Sandbox Code Playgroud)
您需要首先使用NUMTODSINTERVAL函数将示例中的数字转换为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>
Run Code Online (Sandbox Code Playgroud)
基于使用NUMTODSINTERVAL()函数的反向投射,在翻译中会出现一些舍入丢失.