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

Oracle SQL Date to Long e vice-versa


Você está perdendo muita precisão em sua conversão para poder voltar para o outro lado. Você pode se aproximar usando carimbos de data e hora em vez de datas.

Em primeiro lugar, sua consulta inicial estava perdendo completamente o componente de tempo:
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

... mas mesmo com isso, a conversão de volta perdeu muito:
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

Que está mais perto do que o 1432549301782839296 que você tem, mas ainda está muito longe.

Parte do problema é a precisão de DATE , que é apenas para o segundo. Se você usar TIMESTAMP em vez disso, você pode chegar bem perto; você pode ver que o valor a ter é supostamente muito 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

Converter isso de volta é complicado pela aritmética de timestamp que fornece resultados de intervalo, que você precisa manipular para voltar a um número, primeiro como o número original de dias:
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 depois com sua manipulação de poder:
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

O que é bem próximo. Você pode truncar ou arredondar para o número inteiro mais próximo.

Basta olhar para seus números e a manipulação de poder de cada maneira mostra que parece estar dentro da precisão com a qual o Oracle pode lidar:
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...

Mesmo com um carimbo de data/hora, você perde um pouco disso, pois esse primeiro valor está ultrapassando o limite de 9 dígitos fracionários de segundo. A parte que representa os segundos fracionários - depois de contabilizar as 15.895 horas etc. - é .0000089776673785814232865555418862 de um dia, que é .77567046150943497195839881896768 segundos; o carimbo de data/hora está arredondando para .775670462 . Então nunca vai ser perfeito.

Isso também leva a pensar como o número original está sendo gerado; parece improvável que realmente represente um tempo com essa precisão extrema, pois está abaixo de yoctoseconds . Não está muito claro se a 'precisão' é realmente um artefato de ser manipulado com base em potências de 2, mas não parece muito útil de qualquer maneira. É mais comum usar a data de época no estilo Unix, contando segundos ou às vezes milissegundos desde a data de época que você está usando, se tiver que ser armazenado como um número. Este projeto é... interessante.