2010-02-03 4 views
12

Ich habe eine DATE Spalte, die ich auf die nächsten unteren 10 Minuten Intervall in einer Abfrage runden (siehe Beispiel unten).Rund Datum bis 10 Minuten Intervall

Ich schaffte es, indem Sie die Sekunden abgeschnitten und dann die letzte Stelle der Minuten subtrahieren.

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 

Und hier ist das Ergebnis:

01.01.2010 10: 00:00     01.01.2010 10: 00:00
01.01.2010 10: 05 01.01.2010 00     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     2099.01.01 10: 00:00

funktioniert wie erwartet, aber gibt es einen besseren Weg?

EDIT:

Ich war neugierig auf die Leistung, so habe ich den folgenden Test mit 500.000 Zeilen und (nicht wirklich) Zufallsdaten. Ich werde die Ergebnisse als Kommentare zu den bereitgestellten Lösungen hinzufügen.

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; 

Dieser Ansatz dauerte 03.24 s.

+0

Was ist 'SELECT CASE WHEN TO_CHAR (date_col, 'MI') zwischen 0 und 10 THEN TO_DATE (TO_CHAR (date_col, 'YYYY') || '-' || TO_CHAR (Datum_Zeichen, 'MM') || '-' || TO_CHAR (Datum_Zeichen, 'DD') || '' || TO_CHAR (Datum_Zeichen, 'HH') || ': 00' , 'JJJJ-MM-TT HH: MI') END'? –

+0

@OMG Ponys: Sorry, aber das gibt die volle Stunde zurück, wenn '0 <= MI <= 10', sonst' NULL'. –

+0

Ich wollte nicht mit dem Rest der WHEN –

Antwort

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

oder sogar

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

@Tom: Willkommen bei StackOverflow! Beide Ansätze scheinen zu funktionieren, zuerst nahm man '04.18 s', zweite nur' 03.33 s'! Die Leistung Ihrer zweiten Abfrage ist fast die gleiche wie bei meiner ursprünglichen Abfrage, vermeidet aber das 'TO_CHAR', also werde ich Ihre Antwort akzeptieren. Vielen Dank! –

1

Sie könnten den zurückgegebenen Wert als Zeichenfolge und Teilzeichenfolge die linke Seite bis zur letzten Minute Ziffer nehmen und durch eine 0 ersetzen. Ich würde nicht genau sagen, dass es besser ist, wenn Sie nicht eine Art von Metrik bereitstellen.

+1

SELECT SYSDATE, TO_DATE (SUBSTR (TO_CHAR (SYSDATE, 'JJJJMMDD HH24MI'), 1, 12) || '0', 'JJJJMMTT HH24MI') VON DUAL Was er in SQL-Code und auch was er sagte sagte über die Leistung, ich bin mir nicht sicher, was besser wäre, aber Ihre würde einfacher aussehen. – Craig

+0

Siehe Leistungstest in der bearbeiteten Frage. Dieser Ansatz dauerte "04.32 s". –

1

nicht unbedingt besser, aber eine andere Methode:

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

Siehe Leistungstest in der bearbeiteten Frage. Dieser Ansatz dauerte "04.16 s". –

+0

Akzeptiert, weil es kurz und gut lesbar ist. Vielen Dank! Wird bei meiner Lösung bleiben, da es weniger Zeit benötigt. –

+0

@Tony Andrews: Akzeptiert Toms Antwort anstelle von Ihnen, da ich es intuitiver und Leistung besser finde. Hoffe, Sie haben nichts dagegen ... –

3

ich in der Regel Datum Hass zu tun -> Charakter -> Datum Konvertierungen, wenn es nicht notwendig ist. Ich würde lieber Nummern verwenden.

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

Diese extrahiert die Minuten von dem aktuellen Tag, kürzt sie bis in das 10-Minuten-Intervall, und dann fügt sie wieder ein es ein Datum zu machen. Natürlich können Sie sysdate durch ein beliebiges Datum ersetzen. Es vertraut impliziten Conversions viel mehr, als ich will, aber es funktioniert zumindest für jedes NLS-Datumsformat.

+0

Siehe Leistungstest in der bearbeiteten Frage. Dieser Ansatz dauerte '04,39 s'. –

1

Eine andere Methode,

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

Eine Alternative, die schneller sein könnte, wie es um den Anruf trunc entfernt.

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

+1: Ein anderer interessanter Ansatz, der funktioniert. Nahm '04.60 s' in meinem Test, ich denke, der' mod' macht es langsamer als mein Ansatz. –

+0

Ich habe eine Alternative gepostet, die einen Aufruf von trunc entfernt. Es wird interessant sein zu sehen, ob es einen Unterschied in Ihrem Test gibt. –

+0

@ar: Entschuldigung, wurde nicht über Ihr Update benachrichtigt. Ihre aktualisierte Abfrage funktioniert in der Tat besser: '04.22 s' –

0

die nächste obere 10 Minuten Intervall zurückzukehren, habe ich die folgende Abfrage. Ich hoffe, dass es nützlich sein werde, weil ich nicht einfach könnte ein

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

tue ich dies tat und es funktioniert für mich.

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

Dies wird auf this Beitrag basiert.

0

Ich denke, um dies zu lösen, gibt es einen viel einfacheren und schnelleren Weg, um auf ein nächst niedrigeres Intervall von 10 Sekunden, 1 Minute, 10 Minuten usw. zu runden. Versuchen Sie, Ihre Zeitstempel als String mit SUBSTR() so zu manipulieren:

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;