Ответ 1
Хорошо, я обычно не отвечаю на собственные вопросы, но после немного возиться, я окончательно выяснил, как Oracle хранит результат вычитания DATE.
Когда вы вычитаете 2 даты, значение не является типом данных NUMBER (в качестве Oracle 11.2 SQL Reference) вы бы поверили). Внутренний тип данных для вычитания DATE равен 14, который является не документированным внутренним типом данных (NUMBER - внутренний тип данных номер 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 байтов, поскольку мой процессор имеет малоконтинентальную архитектуру, байты обращаются вспять и должны считываться как 1108 или 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
Опять же, поскольку я использую машину little-endian, байты обращаются вспять и должны быть считаны как 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 байтов мы можем их отменить (потому что процессор малоподобный) и получить 84, 96 = 01010100 01100000 base 2 = 21600 в десятичной системе. Преобразование 21600 секунд в часы дает вам 6 часов, это разница, которую мы ожидали.
Надеюсь, это поможет любому, кто задавался вопросом, как фактически вычитается DATE-вычитание.