Oracle
 sql >> Database >  >> RDS >> Oracle

Oracle SQL Date a Long e viceversa

Stai perdendo troppa precisione nella tua conversione per poter tornare indietro. Puoi avvicinarti utilizzando i timestamp anziché le date.

In primo luogo, la tua domanda iniziale stava perdendo completamente la componente temporale:

select to_char(date '1970-01-01'
  + (1432550197431912935 - power(2, 60))/power(2, 44), 'YYYY-MM-DD HH24:MI:SS')
from dual;

2013-07-09 01:13:19

... ma anche con quello, la conversione ha perso troppo:

select ((to_date('2013-07-09 01:13:19','YYYY-MM-DD HH24:MI:SS')
  - date '1970-01-01') * power(2, 44)) + power(2, 60) from dual;

1432550197477589405

Che è più vicino del 1432549301782839296 che hai, ma è ancora abbastanza lontano.

Parte del problema è la precisione di DATE , che è solo al secondo. Se utilizzi TIMESTAMP invece puoi avvicinarti abbastanza; puoi vedere che il valore da avere è presumibilmente molto preciso:

select timestamp '1970-01-01 00:00:00'
  + numtodsinterval((1432550197431912935 - power(2, 60))/power(2, 44), 'DAY')
from dual;

2013-07-09 01:13:18.775670462

La conversione è complicata dall'aritmetica del timestamp che fornisce risultati di intervallo, che devi quindi manipolare per tornare a un numero, prima come numero di giorni originale:

select extract(day from int_val)
  + extract(hour from int_val) / 24
  + extract(minute from int_val) / (24 * 60)
  + extract(second from int_val) / (24 * 60 * 60)
from (
select to_timestamp('2013-07-09 01.13.18.775670462', 'YYYY-MM-DD HH24:MI:SS.FF9')
  - timestamp '1970-01-01 00:00:00' as int_val from dual);

15895.0509117554451620370370370370370371

... e poi con la tua manipolazione del potere:

select ((extract(day from int_val)
    + extract(hour from int_val) / 24
    + extract(minute from int_val) / (24 * 60)
    + extract(second from int_val) / (24 * 60 * 60))
  * power(2, 44)) + power(2, 60)
as x
from (
select to_timestamp('2013-07-09 01.13.18.775670462', 'YYYY-MM-DD HH24:MI:SS.FF9')
  - timestamp '1970-01-01 00:00:00' as int_val from dual);

1432550197431912935.09988554676148148148

Che è dannatamente vicino. Puoi troncarlo o arrotondarlo al numero intero più vicino.

Basta guardare i tuoi numeri e la manipolazione del potere in ogni modo mostra che sembra rientrare nella precisione con cui Oracle può far fronte:

select (1432550197431912935 - power(2, 60)) / power(2, 44)
from dual;

15895.050911755445156359201064333319664

select (15895.050911755445156359201064333319664 * power(2, 44)) + power(2, 60)
from dual;

1432550197431912935.000...

Anche con un timestamp, ne perdi parte, poiché il primo valore sta superando il secondo limite frazionario di 9 cifre. La parte che rappresenta i secondi frazionari - una volta calcolate le 15895 ore ecc. - è .0000089776673785814232865555418862 di un giorno, che è .77567046150943497195839881896768 secondi; il timestamp lo sta arrotondando a .775670462 . Quindi non sarà mai perfetto.

Ciò porta anche a chiedersi come viene generato il numero originale; sembra improbabile che rappresenti effettivamente un tempo fino a tale precisione estrema, poiché è inferiore a yoctoseconds . Non è molto chiaro se la "precisione" sia in realtà un artefatto che viene manipolato in base a poteri di 2, ma non sembra comunque molto utile. È più comune utilizzare la data dell'epoca in stile Unix, contando i secondi o talvolta i millisecondi dalla data dell'epoca che stai utilizzando comunque, se deve essere memorizzata come numero. Questo design è... interessante.