Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Subtraindo datas no Oracle - tipo de dados numérico ou de intervalo?


Ok, normalmente não respondo minhas próprias perguntas, mas depois de alguns ajustes, descobri definitivamente como o Oracle armazena o resultado de uma subtração de DATE.

Quando você subtrai 2 datas, o valor não é um tipo de dados NUMBER (como o manual Oracle 11.2 SQL Reference quer fazer você acreditar). O número do tipo de dados interno de uma subtração DATE é 14, que é um tipo de dados interno não documentado (NUMBER é o tipo de dados interno número 2). No entanto, na verdade, ele é armazenado como 2 números separados com sinal de complemento de dois, com os primeiros 4 bytes usados ​​para representar o número de dias e os últimos 4 bytes usados ​​para representar o número de segundos.

Um exemplo de uma subtração de DATE resultando em uma diferença de inteiro positivo:
select date '2009-08-07' - date '2008-08-08' from dual;

Resulta em:
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

Lembre-se de que o resultado é representado como um complemento de 2 separado de dois números de 4 bytes assinados. Como não há decimais neste caso (364 dias e 0 horas exatamente), os últimos 4 bytes são todos 0s e podem ser ignorados. Para os primeiros 4 bytes, porque minha CPU tem uma arquitetura little-endian, os bytes são invertidos e devem ser lidos como 1.108 ou 0x16c, que é decimal 364.

Um exemplo de uma subtração de DATE resultando em uma diferença de inteiro negativo:
select date '1000-08-07' - date '2008-08-08' from dual;

Resulta em:
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

Novamente, como estou usando uma máquina little-endian, os bytes são invertidos e devem ser lidos como 255.250.97.224, o que corresponde a 11111111 11111010 01100001 11011111. Agora, como isso está na codificação numérica binária assinada em complemento de dois, sabemos que o número é negativo porque o dígito binário mais à esquerda é 1. Para converter isso em um número decimal, teríamos que inverter o complemento de 2 (subtrair 1 e depois fazer o complemento de um) resultando em:00000000 00000101 10011110 00100000 que é igual a -368160 como suspeito.

Um exemplo de uma subtração DATE resultando em uma diferença decimal:
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

A diferença entre essas 2 datas é de 0,25 dias ou 6 horas.
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

Agora desta vez, como a diferença é 0 dias e 6 horas, espera-se que os primeiros 4 bytes sejam 0. Para os últimos 4 bytes, podemos revertê-los (porque a CPU é little-endian) e obter 84,96 =01010100 01100000 base 2 =21600 em decimal. A conversão de 21.600 segundos em horas fornece 6 horas, que é a diferença que esperávamos.

Espero que isso ajude quem estava se perguntando como uma subtração de DATE é realmente armazenada.