приведение (sysdate как дата) не дает точных целочисленных значений - PullRequest
0 голосов
/ 28 февраля 2020

Почему

select (cast(sysdate as date) - to_date('01011970','DDMMYYYY')) * 86400 from dual;

дает значения типа 1582881272,000000000000000000000000000001 или 1582881301,999999999999999999999999999999 вместо точных целочисленных значений?

Я думал, что «приведение (sysdate as date) сокращает фрезу так что * 86400 обеспечивает целочисленные значения.

Мне действительно нужно округлить (!) это?

Ответы [ 3 ]

2 голосов
/ 28 февраля 2020

Глядя на факторы:

86400 = 24 * 60 * 60 = (2³*3)*(2*3*5)*(2*3*5) = 2<sup>5</sup>*3³*5²

Если ваше число секунд с полуночи не кратно 3³ = 27 (так что у вас просто есть коэффициенты 2 и 5 в делителе), то вы получим повторяющееся число, которое не может быть хорошо выражено в виде десятичного числа, поэтому независимо от точности, в которой Oracle хранит результат SYSDATE - DATE '1970-01-01', в какой-то степени будет неточным.

Вы просто необходимо округлить число:

SELECT ROUND((SYSDATE - DATE '1970-01-01')* 86400) FROM DUAL;

Однако, если вы пытаетесь создать unix отметку времени, вам следует использовать:

SELECT ROUND(
         (CAST(SYSTIMESTAMP AT TIME ZONE 'UTC' AS DATE) - DATE '1970-01-01') * 86400
       )
FROM   DUAL;

Поскольку Unix отметки времени находятся в UT C часовой пояс.

Или, если вы хотите сделать это без округления:

SELECT EXTRACT( DAY FROM diff ) * 24 * 60 * 60
       + EXTRACT( HOUR FROM diff ) * 60 * 60
       + EXTRACT( MINUTE FROM diff ) * 60
       + EXTRACT( SECOND FROM diff ) AS unix_timestamp
FROM   (
  SELECT CAST(SYSTIMESTAMP AS TIMESTAMP(0) WITH TIME ZONE)
         - TIMESTAMP '1970-01-01 00:00:00 UTC'
           AS diff
  FROM   DUAL
)

(Приведение используется для удаления дробных секунд, которые SYSTIMESTAMP обычно имеет.)

1 голос
/ 28 февраля 2020

cast(sysdate as date) можно упростить до sysdate, поскольку оно равно уже значением date.

А в Oracle a DATE всегда содержится часть времени, от которой невозможно избавиться.

Единственное, что вы можете сделать, это установить его на полночь, используя trunc(sysdate)

select (trunc(sysdate) - to_date('01011970','DDMMYYYY')) * 86400 
from dual;

Онлайн пример

0 голосов
/ 28 февраля 2020

В моей системе (Oracle 11.2) я вижу ту же проблему:

select (sysdate - date '1970-01-01') * 86400 from dual;

может привести к 1582884027,000000000000000000000000000003, например. Кажется, Oracle использует приблизительный тип (float / double) для вычисления, который не должен.

Очевидное решение:

select round((sysdate - date '1970-01-01') * 86400) from dual;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...