Ok, jag brukar inte svara på mina egna frågor men efter lite pysslande har jag definitivt kommit på hur Oracle lagrar resultatet av en DATUM-subtraktion.
När du subtraherar 2 datum är värdet inte en NUMBER-datatyp (som Oracle 11.2 SQL Reference Manual vill få dig att tro). Det interna datatypnumret för en DATE-subtraktion är 14, vilket är en icke-dokumenterad intern datatyp (NUMBER är intern datatyp nummer 2). Men det lagras faktiskt som två separata två-komplement-signerade nummer, där de första 4 byten används för att representera antalet dagar och de sista 4 byten används för att representera antalet sekunder.
Ett exempel på en DATE-subtraktion som resulterar i en positiv heltalsskillnad:
select date '2009-08-07' - date '2008-08-08' from dual;
Resultat 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
Kom ihåg att resultatet representeras som ett 2-separat två-komplement signerat 4-bytenummer. Eftersom det inte finns några decimaler i det här fallet (364 dagar och 0 timmar exakt), är de sista 4 byten alla 0:or och kan ignoreras. För de första 4 byten, eftersom min CPU har en lite endian-arkitektur, är byten omvända och bör läsas som 1 108 eller 0x16c, vilket är decimal 364.
Ett exempel på en DATE-subtraktion som resulterar i en negativ heltalsskillnad:
select date '1000-08-07' - date '2008-08-08' from dual;
Resultat 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
Återigen, eftersom jag använder en liten-endian-maskin, är byten omvända och ska läsas som 255,250,97,224 vilket motsvarar 11111111 11111010 01100001 11011111. Eftersom detta är i tvås komplement numer så vet vi att det binära numret är encoding. negativ eftersom den binära siffran längst till vänster är en 1. För att omvandla detta till ett decimaltal måste vi vända på 2:ans komplement (subtrahera 1 och gör sedan ettans komplement) vilket resulterar i:00000000 00000101 10011110 00100000 vilket är lika med -36816 som misstänkt. P>
Ett exempel på en DATE-subtraktion som resulterar i en decimalskillnad:
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
Skillnaden mellan dessa två datum är 0,25 dagar eller 6 timmar.
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 den här gången, eftersom skillnaden är 0 dagar och 6 timmar, förväntas det att de första 4 byten är 0. För de sista 4 byten kan vi vända på dem (eftersom CPU är little-endian) och få 84,96 =01010100 01100000 bas 2 =21600 i decimal. Att konvertera 21600 sekunder till timmar ger dig 6 timmar vilket är skillnaden som vi förväntade oss.
Hoppas detta hjälper alla som undrade hur en DATE-subtraktion faktiskt lagras.