Oracle SQL разница во времени в формате ЧЧ: ММ: СС - PullRequest
2 голосов
/ 20 февраля 2020

Я пытаюсь получить разницу во времени в базе данных Oracle. Разница во времени для всех строк в порядке, за исключением одной строки. Вот запрос

SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls

Разница во времени возникает в следующей строке:

call_end: '2020-02-20 13: 00: 20'
call_start: '2020- 02-20 12: 56: 03 '

Возвращенный результат -' 11: 55: 43 ', что неверно. Поскольку правильный ответ должен быть '00: 04: 17 '

Ответы [ 5 ]

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

Похоже, ваша таблица уже хранит время начала / окончания звонка как даты, и вы выполняете неявное преобразование из даты в строку, а затем конвертируете обратно в дату. Вы можете увидеть этот результат, если у вас есть HH вместо HH24 в настройке NLS_DATE_FORMAT:

alter session set nls_date_format = 'YYYY-MM-DD HH:MI:SS';

with calls (call_end, call_start) as (
  select cast(timestamp '2020-02-20 13:00:20' as date), cast(timestamp '2020-02-20 12:56:03' as date) from dual
)
SELECT MAX(REGEXP_SUBSTR (CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;

CALL_TIME                  
---------------------------
11:55:43

Когда вы делаете

TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS')

, потому что это уже дата, которую вы действительно делаете:

TO_DATE(TO_CHAR(call_end, <NLS_DATE_FORMAT>), 'YYYY/MM/DD HH24:MI:SS')

поэтому с моими настройками (и ваши должны быть похожими, возможно, с косыми чертами вместо черточек), то есть:

TO_DATE(TO_CHAR(call_end, 'YYYY-MM-DD HH:MI:SS'), 'YYYY/MM/DD HH24:MI:SS')

, и несоответствие между ЧЧ и ЧЧ24 становится более очевидным. Итак, вы на самом деле конвертируете строки 2020-02-20 01:00:20 и 2020-02-20 12:56:03 обратно в даты, а разницу во времени между 01:00:20 и 12:56:03 11:55:43. Ну, на самом деле, это минус 11 часов:

SELECT CAST(TO_DATE(call_end, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP) - CAST(TO_DATE(call_start, 'YYYY/MM/DD HH24:MI:SS') AS TIMESTAMP)
FROM calls;

CAST(TO_DATE(CALL_E
-------------------
-00 11:55:43.000000

, но ваше регулярное выражение не подхватывает это.

Поскольку они являются датами, пропустите эту часть преобразование полностью и просто приведение непосредственно к временным меткам, если вы хотите, чтобы интервалы работали с:

SELECT MAX(REGEXP_SUBSTR (CAST(call_end AS TIMESTAMP) - CAST(call_start AS TIMESTAMP), '\d{2}:\d{2}:\d{2}')) AS call_time
FROM calls;

CALL_TIME                  
---------------------------
00:04:17

или используйте подстроку вместо регулярного выражения, как показало @MTO.

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

SELECT TO_CHAR(date '2000-01-01' + MAX(call_end - call_start), 'HH24:MI:SS') AS call_time
FROM calls;

CALL_TIM
--------
00:04:17

Это не будет работать должным образом для звонок, который длится более 24 часов (раньше видел это много с модемными звонками, но все же может происходить); но и ваш интервал не приблизится. Оба игнорируют любые полные дни и просто показывают остаток. Конечно, есть способы справиться с этим, но вам нужно решить, как вы хотите его отображать - с отдельным счетчиком дней (как в любом случае интервал) или с числом часов, разрешенным go выше 24 .. ... но тогда вы можете go превысить 99 часов ...


Ваш вопрос показывает время окончания вызова как 2020-02-20 13:00:20, что говорит о том, что ваш клиент отображает это когда вы запрашиваете таблицу. Некоторые клиенты (я думаю, PL / SQL Developer, но не уверен, что это было давно) используют свои собственные настройки / настройки вместо соблюдения настроек NLS сеанса. Но это никак не влияет на внутреннее поведение Oracle, когда приходится выполнять неявные преобразования.

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

Пожалуйста, смотрите ответ ниже, но это будет работать только в течение 24 часов и возвращает оба значения '00'

WITH sample_lt AS(
                            SELECT '2020-02-20 12:56:03' START_TIME, '2020-02-20 13:00:20' END_TIME FROM dual
                                 )
                    SELECT start_time,
                            end_time,
                                TO_CHAR (TRUNC (SYSDATE) + (to_date(end_time, 'yyyy-mm-dd HH24:MI:SS') -
                                  to_date(start_time, 'yyyy-mm-dd HH24:MI:SS')
                                 ) , 'hh24:mi:ss' ) duration
                    FROM sample_lt ;
0 голосов
/ 20 февраля 2020

Предполагая, что ваши типы данных являются строками (вы действительно должны хранить их как DATE типы данных), вы можете использовать:

SELECT SUBSTR(
         MAX(
           ( TO_DATE( call_end,   'YYYY-MM-DD HH24:MI:SS' )
           - TO_DATE( call_start, 'YYYY-MM-DD HH24:MI:SS' )
           ) DAY(1) TO SECOND
         ),
         4,
         8
       ) AS call_time
FROM   calls

Если они уже DATE типов данных, то в MAX агрегация, просто используйте:

( call_end - call_start ) DAY(1) TO SECOND

Итак, для ваших данных:

CREATE TABLE calls ( call_end, call_start ) AS
SELECT '2020-02-20 13:00:20', '2020-02-20 12:56:03' FROM DUAL

Это выводит:

| CALL_TIME |
| :-------- |
| 00:04:17  |

db <> скрипка здесь

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

Мистер Джил, Вы также можете сделать: db <> fiddle : Это решение также будет учитывать, когда вызов истек через несколько дней, добавляя эти часы вместе:

WITH da AS (
SELECT
    NUMTODSINTERVAL(TO_DATE('2020-02-20 13:00:20', 'yyyy-mm-dd hh24:mi:ss') - TO_DATE('2020-02-20 12:56:03', 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
    dual ) SELECT
    EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) DIFFERENCE
FROM
    da
DIFFERENCE|
----------|
0:4:17    |

Так что вы можете реализовать как

WITH da AS (    
SELECT
    NUMTODSINTERVAL(TO_DATE(call_end, 'yyyy-mm-dd hh24:mi:ss') - TO_DATE(call_start, 'yyyy-mm-dd hh24:mi:ss'), 'DAY') AS call_diff
FROM
    calls
    ) SELECT
    EXTRACT( DAY FROM call_diff )*24 + EXTRACT( HOUR FROM call_diff )|| ':' || EXTRACT( MINUTE FROM call_diff ) || ':' || EXTRACT( SECOND FROM call_diff ) TIMESTAMP
FROM
    da
0 голосов
/ 20 февраля 2020

Я не уверен, хотите ли вы получить результат в виде интервала или метки времени, но это должно делать то, что вы хотите:

select t.*, call_end - call_start,
       substr(to_char(call_end - call_start, 'HH24:MI:SS'), 12, 8) as str    from (select timestamp '2020-02-20 13:00:20.000' as call_end,
             timestamp '2020-02-20 12:56:03.000' as call_start
      from dual) t

Здесь - это db <> скрипка.

...