Ok, jeg svarer normalt ikke på mine egne spørgsmål, men efter lidt fifleri har jeg fundet ud af, hvordan Oracle gemmer resultatet af en DATO-subtraktion.
Når du trækker 2 datoer fra, er værdien ikke en NUMBER-datatype (som Oracle 11.2 SQL Reference-manualen vil have dig til at tro). Det interne datatypenummer for en DATO-subtraktion er 14, som er en ikke-dokumenteret intern datatype (NUMBER er intern datatype nummer 2). Det er dog faktisk gemt som 2 separate to-komplement-signerede numre, hvor de første 4 bytes bruges til at repræsentere antallet af dage og de sidste 4 bytes bruges til at repræsentere antallet af sekunder.
Et eksempel på en DATE-subtraktion, der resulterer i en positiv heltalsforskel:
select date '2009-08-07' - date '2008-08-08' from dual;
Resultater i:
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
Husk, at resultatet er repræsenteret som et 2-separat to-komplement med 4 byte-numre. Da der ikke er nogen decimaler i dette tilfælde (364 dage og 0 timer nøjagtigt), er de sidste 4 bytes alle 0'er og kan ignoreres. For de første 4 bytes, fordi min CPU har en little-endian arkitektur, er bytes omvendt og skal læses som 1.108 eller 0x16c, hvilket er decimal 364.
Et eksempel på en DATE-subtraktion, der resulterer i en negativ heltalsforskel:
select date '1000-08-07' - date '2008-08-08' from dual;
Resultater i:
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
Igen, da jeg bruger en lille-endian-maskine, er bytes omvendt og skal læses som 255,250,97,224, hvilket svarer til 11111111 11111010 01100001 11011111. Nu, da dette er i to's komplement tallet, så kendte vi det binære tal, som er encoding. negativt, fordi det binære ciffer længst til venstre er et 1. For at konvertere dette til et decimaltal skal vi vende 2'erens komplement (træk 1 fra, og lav derefter ens komplement), hvilket resulterer i:00000000 00000101 10011110 00100000, hvilket er lig med -36816. P>
Et eksempel på en DATE-subtraktion, der resulterer i en decimalforskel:
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
Forskellen mellem disse 2 datoer er 0,25 dage eller 6 timer.
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
Nu denne gang, da forskellen er 0 dage og 6 timer, forventes det, at de første 4 bytes er 0. For de sidste 4 bytes kan vi vende dem (fordi CPU er little-endian) og få 84,96 =01010100 01100000 grundtal 2 =21600 i decimal. Konvertering af 21600 sekunder til timer giver dig 6 timer, hvilket er den forskel, vi forventede.
Håber dette hjælper enhver, der undrede sig over, hvordan en DATE-subtraktion faktisk gemmes.