Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Oracle SQL Date zu Long und umgekehrt

Sie verlieren zu viel Präzision in Ihrer Konvertierung, um in die andere Richtung zurückkehren zu können. Sie können näher herankommen, indem Sie Zeitstempel anstelle von Daten verwenden.

Erstens ging bei Ihrer ursprünglichen Abfrage die Zeitkomponente vollständig verloren:

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

... aber selbst damit hat die Rückkonvertierung viel zu viel verloren:

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

Das ist näher als 1432549301782839296, das Sie erhalten haben, aber immer noch ziemlich weit weg.

Ein Teil des Problems ist die Genauigkeit von DATE , das ist nur auf die Sekunde. Wenn Sie TIMESTAMP verwenden stattdessen können Sie ziemlich nahe kommen; Sie können sehen, dass der zu habende Wert angeblich sehr genau ist:

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

Das Zurückrechnen wird durch die Zeitstempelarithmetik erschwert, die Intervallergebnisse liefert, die Sie dann manipulieren müssen, um zu einer Zahl zurückzukehren, zuerst als die ursprüngliche Anzahl von Tagen:

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

... und dann mit deiner Machtmanipulation:

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

Was verdammt nah ist. Sie können das auf die nächste ganze Zahl kürzen oder runden.

Ein Blick auf Ihre Zahlen und die Leistungsmanipulation zeigt, dass es innerhalb der Genauigkeit zu liegen scheint, mit der Oracle fertig werden kann:

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

Selbst mit einem Zeitstempel geht etwas davon verloren, da dieser erste Wert die 9-stellige Sekundenbruchteilgrenze überschreitet. Der Teil, der die Sekundenbruchteile darstellt – nachdem Sie die 15895 Stunden usw. berücksichtigt haben – ist .0000089776673785814232865555418862 eines Tages, also .77567046150943497195839881896768 Sekunden; der Zeitstempel rundet das auf .775670462 . Es wird also nie perfekt sein.

Das führt auch dazu, sich zu fragen, wie die ursprüngliche Nummer generiert wird; Es scheint unwahrscheinlich, dass es tatsächlich eine Zeit bis zu dieser extremen Genauigkeit darstellt, da es unter Yoktosekunden . Es ist nicht wirklich klar, ob die "Präzision" tatsächlich ein Artefakt ist, das auf der Grundlage von Potenzen von 2 manipuliert wurde, aber es sieht sowieso nicht sehr nützlich aus. Es ist üblicher, das Epochendatum im Unix-Stil zu verwenden, wobei Sekunden oder manchmal Millisekunden seit dem Epochendatum gezählt werden, das Sie sowieso verwenden, wenn es überhaupt als Zahl gespeichert werden muss. Dieses Design ist... interessant.