Оракул дата округления - PullRequest
3 голосов
/ 27 мая 2009

Этот звонок -

SELECT ((TO_DATE ('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS') - TO_DATE ('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS')) * 1440.000) from dual

Создает результат 95 - это то, что вы ожидаете от расчета.

Этот звонок -

SELECT trunc((TO_DATE ('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS') - TO_DATE ('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS')) * 1440.000) from dual

дает результат 94.

Предлагаемое исправление - SELECT trunc((TO_DATE ('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS') - TO_DATE ('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS')) * 1440.000+.00001) from dual

Это решение мне кажется неправильным - кто-нибудь может предложить лучший вариант? Фактическое требование состоит в том, чтобы считать целые минуты разницы между двумя датами.

Ответы [ 4 ]

4 голосов
/ 27 мая 2009

Забудьте даты и используйте временные метки. Используя арифметику временных меток, Oracle использует тип данных INTERVAL, чтобы избежать проблемы округления.

select extract (minute from cast(later as timestamp) - cast(earlier as timestamp)) +
       (extract (hour from cast(later as timestamp) - cast(earlier as timestamp)) * 60)
from
(select TO_DATE ('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS') later,
       TO_DATE ('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS') earlier
from dual)
/

Если даты более одного дня, вам нужно будет учесть это тоже.

4 голосов
/ 27 мая 2009

Отличный вопрос.

Арифметика даты не совсем точна из-за преобразований типов данных здесь.

TO_DATE('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS')
- TO_DATE('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS'))
= .0659722222222222222222222222222222222222000000000000000

Но

.0659722222222222222222222222222222222222000000000000000
* 1440
=94.9999999999999999999999999999999999999700000000000

Это означает, что ни один из TRUNC, ROUND, CEIL и FLOOR не будет работать во всех случаях.

Чтобы решить эту проблему, вы должны иметь возможность преобразовать каждую дату в целое число, прежде чем выполнять арифметику с ней, например ::

select FLOOR((TO_CHAR(TO_DATE('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS'),'J') * 1440
+ TO_CHAR(TO_DATE('06/06/2009 16:00:00', 'DD/MM/YYYY HH24:MI:SS'),'SSSSS') / 60)
- (TO_CHAR(TO_DATE('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS'),'J') * 1440
+ TO_CHAR(TO_DATE('06/06/2009 14:25:00', 'DD/MM/YYYY HH24:MI:SS'),'SSSSS') / 60))
from dual;
0 голосов
/ 27 мая 2009

Ну, не лучше, но вы пробовали FLOOR вместо TRUNC? (В сумасшедшем случае, когда это работает, вы можете проверить, является ли результат отрицательным, и в этом случае вам нужно добавить его, потому что FLOOR «округляет» в сторону отрицательной бесконечности, а TRUNC округляет в сторону нуля. не будет уродливее, чем добавление доли секунды перед использованием TRUNC.)

0 голосов
/ 27 мая 2009

Почему бы не использовать ROUND вместо TRUNC? Он дает результат до ближайшего дня применительно к датам, ближайшего целого числа применительно к числам (TRUNC усекает, и, похоже, здесь возникают странные арифметические проблемы).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...