好的,我通常不回答自己的问题,但经过一些尝试后,我确定了Oracle如何存储DATE减法结果的方法。
当你对两个日期进行减法运算时,结果不是NUMBER数据类型(正如Oracle 11.2 SQL参考手册所说)。DATE减法的内部数据类型是14,这是一个未经记录的内部数据类型(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
记得这个结果是由两个独立的二进制补码表示的4字节数字组成的。由于在这种情况下没有小数(刚好为364天和0小时),最后4字节都为0,可以忽略不计。对于前4字节,因为我的CPU采用小端架构,所以字节被反转,应该读作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,正如我们所料。
一个日期减法的例子,得到一个十进制差值:
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,基数为2,相当于十进制的21600。将21600秒转换为小时,得到6小时,这正是我们预期的差异。
希望这能帮助那些想知道日期相减实际上是如何存储的人。