Oracle
 sql >> Base de données >  >> RDS >> Oracle

Soustraire des dates dans Oracle - Type de données Nombre ou Intervalle ?

Ok, je ne réponds pas normalement à mes propres questions mais après un peu de bricolage, j'ai définitivement compris comment Oracle stocke le résultat d'une soustraction de DATE.

Lorsque vous soustrayez 2 dates, la valeur n'est pas un type de données NUMBER (comme le manuel Oracle 11.2 SQL Reference voudrait vous le faire croire). Le numéro de type de données interne d'une soustraction DATE est 14, qui est un type de données interne non documenté (NUMBER est le numéro de type de données interne 2). Cependant, il est en fait stocké sous la forme de 2 nombres signés complémentaires à deux séparés, les 4 premiers octets étant utilisés pour représenter le nombre de jours et les 4 derniers octets utilisés pour représenter le nombre de secondes.

Un exemple d'une soustraction DATE résultant en une différence entière positive :

select date '2009-08-07' - date '2008-08-08' from dual;

Résultats en :

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

Rappelez-vous que le résultat est représenté sous la forme de 2 nombres séparés en complément à deux signés sur 4 octets. Puisqu'il n'y a pas de décimales dans ce cas (364 jours et 0 heures exactement), les 4 derniers octets sont tous des 0 et peuvent être ignorés. Pour les 4 premiers octets, parce que mon CPU a une architecture little-endian, les octets sont inversés et doivent être lus comme 1 108 ou 0x16c, qui est décimal 364.

Un exemple d'une soustraction DATE résultant en une différence entière négative :

select date '1000-08-07' - date '2008-08-08' from dual;

Résultats en :

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

Encore une fois, puisque j'utilise une machine little-endian, les octets sont inversés et doivent être lus comme 255,250,97,224, ce qui correspond à 11111111 11111010 01100001 11011111. Maintenant, comme il s'agit d'un codage numérique binaire signé en complément à deux, nous savons que le nombre est négatif car le chiffre binaire le plus à gauche est un 1. Pour convertir cela en un nombre décimal, nous devrions inverser le complément à 2 (soustraire 1 puis faire le complément à un) résultant en :00000000 00000101 10011110 00100000 qui équivaut à -368160 comme suspect.

Un exemple d'une soustraction DATE résultant en une différence décimale :

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

La différence entre ces 2 dates est de 0,25 jours ou 6 heures.

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

Maintenant cette fois, puisque la différence est de 0 jours et 6 heures, on s'attend à ce que les 4 premiers octets soient 0. Pour les 4 derniers octets, on peut les inverser (car le CPU est little-endian) et obtenir 84,96 =01010100 01100000 base 2 =21600 en décimal. La conversion de 21600 secondes en heures vous donne 6 heures, ce qui est la différence à laquelle nous nous attendions.

J'espère que cela aidera tous ceux qui se demandaient comment une soustraction DATE est réellement stockée.