sql >> Databasteknik >  >> RDS >> Oracle

Oracle SQL Date to Long och vice versa

Du tappar för mycket precision i din konvertering för att kunna gå tillbaka åt andra hållet. Du kan komma närmare genom att använda tidsstämplar istället för datum.

För det första förlorade din första fråga tidskomponenten helt:

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

... men även med det har konverteringen tillbaka tappat alldeles för mycket:

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

Vilket är närmare än de 1432549301782839296 du har, men ändå ganska långt kvar.

En del av problemet är precisionen i DATE , vilket bara är till den andra. Om du använder TIMESTAMP istället kan man komma ganska nära; du kan se att värdet att ha är förmodligen mycket exakt:

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

Att konvertera tillbaka är komplicerat av tidsstämpelaritmetik som ger intervallresultat, som du sedan måste manipulera för att komma tillbaka till ett tal, först som det ursprungliga antalet dagar:

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

... och sedan med din maktmanipulation:

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

Vilket är ganska nära. Du kan avkorta eller avrunda det till närmaste heltal.

Bara att titta på dina siffror och maktmanipulationen åt olika håll visar att det verkar ligga inom den precision som Oracle kan klara av:

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

Även med en tidsstämpel förlorar du en del av det, eftersom det första värdet går förbi den 9-siffriga bråkdelssekundgränsen. Den del som representerar bråksekunderna - när du väl har räknat med de 15895 timmarna etc. - är .0000089776673785814232865555418862 av en dag, vilket är .77567046150943497195839881896768 sekunder; tidsstämpeln avrundar det till .775670462 . Så det kommer aldrig att bli perfekt.

Det leder också till att man undrar hur det ursprungliga numret genereras; det verkar osannolikt att det faktiskt representerar en tid ner till den extrema precisionen, eftersom det är under yoktosekunder . Det är inte riktigt klart om "precisionen" faktiskt är en artefakt av att den manipuleras baserat på potenserna 2, men det ser inte särskilt användbart ut ändå. Det är vanligare att använda epokdatumet i Unix-stil, räknar sekunder eller ibland millisekunder sedan epokdatumet du ändå använder, om det överhuvudtaget måste lagras som ett tal. Den här designen är...intressant.



  1. Inkludera alla ID i ActiveRecord-frågan

  2. Nästa närmaste datum och tid i MySQL

  3. Använda OracleParameter med C# TimeSpan - Literal matchar inte

  4. Hur väljer man den n:e raden i en SQL-databastabell?