Круглая дата с 10-минутным интервалом - PullRequest
14 голосов
/ 03 февраля 2010

У меня есть столбец DATE, который я хочу округлить до следующего нижнего 10-минутного интервала в запросе (см. Пример ниже).

Мне удалось сделать это путем усечения секунд, а затем вычитанияпоследняя цифра минут.

WITH test_data AS (
        SELECT TO_DATE('2010-01-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:05:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:09:59', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:10:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2099-01-01 10:00:33', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
)
-- #end of test-data
SELECT
  d, TRUNC(d, 'MI') - MOD(TO_CHAR(d, 'MI'), 10) / (24 * 60)
FROM test_data

И вот результат:

01.01.2010 10: 00: 00 01.01.2010 10: 00: 00 01.01.2010 10: 05: 00 01.01.2010 10: 00: 00 01.01.2010 10: 09: 59 01.01.2010 10: 00: 00 01.01.2010 10: 10: 00 01.01.2010 10: 10: 00 01.01.2099 10: 00: 33 01.01.2099 10: 00: 00

Работает, как ожидается, но есть ли лучший способ?

EDIT :

Мне было интересно узнать производительность, поэтому я провел следующий тест с 500 000 строк и (не совсем) случайных дат.Я собираюсь добавить результаты в качестве комментариев к предоставленным решениям.

DECLARE
  t       TIMESTAMP := SYSTIMESTAMP;
BEGIN
  FOR i IN (
    WITH test_data AS (
      SELECT SYSDATE + ROWNUM / 5000 d FROM dual
      CONNECT BY ROWNUM <= 500000
    )
    SELECT TRUNC(d, 'MI') - MOD(TO_CHAR(d, 'MI'), 10) / (24 * 60)
    FROM test_data
  )
  LOOP
    NULL;
  END LOOP;
  dbms_output.put_line( SYSTIMESTAMP - t );
END;

Этот подход занял 03.24 s.

Ответы [ 7 ]

11 голосов
/ 15 апреля 2010
select
  trunc(sysdate, 'mi')
  - numtodsinterval(mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10), 'minute')
from dual;

или даже

select
  trunc(sysdate, 'mi')
  - mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10) / (24 * 60)
from dual;
4 голосов
/ 03 февраля 2010

Я вообще ненавижу делать дату -> символ -> преобразование даты, когда в этом нет необходимости. Я бы лучше использовал цифры.

select trunc((sysdate - trunc(sysdate))*60*24,-1)/(60*24)+trunc(sysdate) from dual;     

Это извлекает минуты текущего дня, усекает их до 10-минутного интервала, а затем добавляет их обратно, чтобы снова сделать дату. Конечно, вы можете заменить sysdate на любую дату, какую захотите. Он доверяет неявным преобразованиям намного больше, чем я хочу, но, по крайней мере, он будет работать для любого формата даты NLS.

1 голос
/ 17 марта 2010

Другой метод,

select my_date - mod( (my_date-trunc(my_date))*24*60, 10)/24/60
from (
  select sysdate my_date from dual
);

Альтернатива, которая может быть быстрее, так как удаляет вызов trunc.

select my_date - mod( (my_date-to_date('1970', 'yyyy'))*24*60, 10)/24/60
from (
  select sysdate my_date from dual
);
1 голос
/ 03 февраля 2010

Не обязательно лучше, но другой метод:

WITH test_data AS (
        SELECT TO_DATE('2010-01-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:05:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:09:59', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2010-01-01 10:10:00', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
  UNION SELECT TO_DATE('2099-01-01 10:00:33', 'YYYY-MM-DD HH24:MI:SS') d FROM dual
)
-- #end of test-data
SELECT
  d, TRUNC(d) + FLOOR((d-TRUNC(d))*24*6)/(24*6)
FROM test_data
1 голос
/ 03 февраля 2010

Вы можете взять возвращаемое значение в виде строки и подстроку с левой стороны до последней минуты и заменить ее на 0. Я бы точно не сказал, что лучше, если вы не предоставите какую-то метрику.

0 голосов
/ 19 октября 2015

Я думаю, что для решения этой проблемы есть гораздо более простой и быстрый способ округления до следующего меньшего 10 секунд, 1 минуты, 10 минут и т. Д. Попробуйте манипулировать вашей отметкой времени как строкой, используя SUBSTR () следующим образом:

SELECT 
SUBSTR(datetime(d),1,18)||'0' AS Slot10sec, 
SUBSTR(datetime(d),1,17)||'00' AS Slot1min, 
SUBSTR(datetime(d),1,15)||'0:00' AS Slot10min, 
SUBSTR(datetime(d),1,14)||'00:00' AS Slot1h, 
MY_VALUE
FROM MY_TABLE;
0 голосов
/ 20 марта 2015

Чтобы вернуть следующий верхний 10-минутный интервал, я использовал следующий запрос.Я надеюсь, что это будет полезно, потому что я не мог просто сделать

trunc(sysdate, 'mi') + mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10) / (24 * 60)

Я сделал это, и это сработало для меня.

select 
case when mod(EXTRACT(minute FROM cast(sysdate as timestamp)),5) between 1 and 4
      then trunc(sysdate,'mi')+((5*TRUNC(EXTRACT(minute FROM cast(sysdate as timestamp))/5,
0)+5)-EXTRACT(minute FROM cast(sysdate as timestamp)))/1440
      else trunc(sysdate,'mi')
end
from dual

Это основано на этом сообщении.

...