2010-02-03 22 views
12

Tengo una columna DATE que quiero redondear al siguiente intervalo inferior de 10 minutos en una consulta (ver ejemplo a continuación).Fecha de la ronda a intervalo de 10 minutos

Logré hacerlo truncando los segundos y luego restando el último dígito de minutos.

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 

Y aquí está el resultado:

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

funciona como se espera, pero hay una manera mejor?

EDITAR:

tenía curiosidad sobre el rendimiento, así que lo hice la siguiente prueba con 500.000 filas y (no realmente) fechas al azar. Voy a agregar los resultados como comentarios a las soluciones proporcionadas.

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; 

Este enfoque tomó 03.24 s.

+0

¿Qué hay de 'SELECT CASE CUANDO TO_CHAR (date_col, 'MI') entre 0 y 10, entonces TO_DATE (TO_CHAR (date_col, 'YYYY') || '-' || TO_CHAR (date_col, 'MM') || '-' || TO_CHAR (date_col, 'DD') || '' || TO_CHAR (date_col, 'HH') || ': 00' , 'AAAA-MM-DD HH: MI') END'? –

+0

@OMG Ponies: Lo siento, pero eso devuelve la hora completa cuando '0 <= MI <= 10', de lo contrario' NULL'. –

+0

No quería saturar el resto de CUANDO –

Respuesta

10
select 
    trunc(sysdate, 'mi') 
    - numtodsinterval(mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10), 'minute') 
from dual; 

o incluso

select 
    trunc(sysdate, 'mi') 
    - mod(EXTRACT(minute FROM cast(sysdate as timestamp)), 10)/(24 * 60) 
from dual; 
+0

@Tom: ¡Bienvenido a StackOverflow! Ambos enfoques parecen funcionar, primero uno tomó '04.18 s', el segundo solo' 03.33 s'! El rendimiento de su segunda consulta es casi el mismo que con mi consulta original, pero evita el 'TO_CHAR', así que voy a aceptar su respuesta. ¡Gracias! –

1

puede tomar el valor devuelto como una cadena y subcadena el lado izquierdo hasta el último dígito de los minutos y reemplazarlo con un 0. No diría exactamente que eso sea mejor a menos que proporciones algún tipo de métrica.

+1

SELECCIONAR SYSDATE, TO_DATE (SUBSTR (TO_CHAR (SYSDATE, 'YYYYMMDD HH24MI'), 1, 12) || '0', 'YYYYMMDD HH24MI') DE DUAL Lo que dijo en SQL Code y También lo que dijo sobre el rendimiento, no estoy seguro de cuál sería mejor, pero el suyo se vería más simple. – Craig

+0

Ver la prueba de rendimiento en la pregunta editada. Este enfoque tomó '04.32 s'. –

1
No

necesariamente mejor, pero otro método:

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 
+0

Ver la prueba de rendimiento en la pregunta editada. Este enfoque tomó '04.16 s'. –

+0

Aceptado porque es corto y bastante legible. ¡Gracias! Sin embargo, me quedaré con mi solución, ya que lleva menos tiempo. –

+0

@Tony Andrews: acepté la respuesta de Tom en lugar de la tuya, ya que la encuentro más intuitiva y el rendimiento es mejor. Espero que no te importe ... –

3

lo general odio hacer la fecha -> carácter -> conversiones de fecha cuando no es necesario. Prefiero usar números.

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

Esto extrae los minutos del día actual, les trunca hasta el intervalo de 10 minutos, y luego se suma de nuevo a hacer que sea una fecha de nuevo. Por supuesto, puede reemplazar sysdate con la fecha que desee. Confía en las conversiones implícitas mucho más de lo que quiero, pero al menos funcionará para cualquier formato de fecha NLS.

+0

Ver la prueba de rendimiento en la pregunta editada. Este enfoque tomó '04.39 s'. –

1

Otro método,

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

Una alternativa que podría ser más rápido, ya que elimina el llamado a trunc.

select my_date - mod((my_date-to_date('1970', 'yyyy'))*24*60, 10)/24/60 
from (
    select sysdate my_date from dual 
); 
+0

+1: Otro enfoque interesante que funciona. Tomó '04.60 s' en mi prueba, supongo que el' mod' lo hace más lento que mi enfoque. –

+0

He publicado una alternativa, que elimina una llamada a trunc. Será interesante ver si hay alguna diferencia en su prueba. –

+0

@ar: Lo sentimos, no me notificaron sobre su actualización. Su consulta actualizada funciona mejor: '04.22 s' –

0

Para volver el siguiente intervalo superior 10 minutos, he utilizado la siguiente consulta. Yo espero que sea útil, ya que no podía simplemente hacer una

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

lo hice y funcionó para mí.

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

Esto se basa en this puesto.

0

Creo que para resolver esto hay una forma mucho más fácil y más rápida de redondear a los siguientes intervalos de 10 segundos, 1 minuto, 10 minutos, etc. Tratar de manipular su marca de tiempo como una cadena usando SUBSTR() así:

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; 
Cuestiones relacionadas