У меня есть a 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
Работы как ожидалось, но там лучший путь?
Править:
Мне было любопытно на предмет производительности, таким образом, я сделал следующий тест с 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
.
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;
Я вообще ненавижу преобразовывать дату -> символ -> дату, когда в этом нет необходимости. Я бы предпочел использовать числа.
select trunc((sysdate - trunc(sysdate))*60*24,-1)/(60*24)+trunc(sysdate) from dual;
Это извлекает минуты из текущего дня, обрезает их до 10-минутного интервала, а затем добавляет их обратно, чтобы снова сделать дату. Конечно, вы можете заменить sysdate любой датой, какой захотите. Он доверяет неявным преобразованиям намного больше, чем я хочу, но, по крайней мере, он будет работать для любого формата даты NLS.
Вы можете взять возвращаемое значение как строку и подстроку с левой стороны до последней цифры минуты и заменить ее на 0
. Я бы не сказал, что это лучше, если вы не предоставите какую-то метрику.
Не обязательно лучше, но другой метод:
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
Другой метод,
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
);