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

Oracle SQL Date to Long et vice versa

Vous perdez trop de précision dans votre conversion pour pouvoir revenir dans l'autre sens. Vous pouvez vous rapprocher en utilisant des horodatages au lieu de dates.

Tout d'abord, votre requête initiale perdait complètement la composante temporelle :

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

... mais même avec cela, la reconversion a beaucoup trop perdu :

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

Ce qui est plus proche que le 1432549301782839296 que vous avez, mais encore assez loin.

Une partie du problème est la précision de DATE , qui n'est qu'à la seconde. Si vous utilisez TIMESTAMP au lieu de cela, vous pouvez vous approcher assez près; vous pouvez voir que la valeur à avoir est censée être très précise :

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 reconversion est compliquée par l'arithmétique d'horodatage donnant des résultats d'intervalle, que vous devez ensuite manipuler pour revenir à un nombre, d'abord comme le nombre de jours d'origine :

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

... et puis avec votre manipulation de puissance :

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

Ce qui est sacrément proche. Vous pouvez tronquer ou arrondir cela au nombre entier le plus proche.

Le simple fait de regarder vos chiffres et la manipulation de puissance dans chaque sens montre que cela semble être dans la précision qu'Oracle peut gérer :

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...

Même avec un horodatage, vous en perdez une partie, car cette première valeur dépasse la limite de seconde fractionnaire à 9 chiffres. La partie qui représente les fractions de seconde - une fois que vous avez pris en compte les 15895 heures, etc. - est .0000089776673785814232865555418862 d'un jour, qui est .77567046150943497195839881896768 secondes ; l'horodatage arrondit cela à .775670462 . Ce ne sera donc jamais parfait.

Cela conduit également à se demander comment le nombre d'origine est généré ; il semble peu probable qu'il représente réellement un temps jusqu'à cette extrême précision, car il est inférieur à yoctosecondes . Il n'est pas vraiment clair si la "précision" est en fait un artefact de sa manipulation basée sur des puissances de 2, mais cela ne semble de toute façon pas très utile. Il est plus courant d'utiliser la date d'époque de style Unix, en comptant les secondes ou parfois les millisecondes depuis la date d'époque que vous utilisez de toute façon, si elle doit être stockée sous forme de nombre. Ce dessin est... intéressant.