2010-02-03 11 views
12

Mam kolumnę DATE, którą chcę zaokrąglić do następnej niższej 10-minutowej przerwy w zapytaniu (patrz przykład poniżej).Okrągła data do przedziału 10 minut

Udało mi się to zrobić obcinając sekundy, a następnie odejmując ostatnią cyfrę minut.

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 

A oto wynik:

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

działa zgodnie z oczekiwaniami, ale tam jest lepszy sposób?

EDIT:

byłem ciekaw wyników, więc zrobiłem następujący test z 500.000 wierszy i (naprawdę nie) przypadkowych terminach. Zamierzam dodać wyniki jako komentarze do dostarczonych rozwiązań.

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; 

Podejście to zajęło 03.24 s.

+0

Co o 'SELECT CASE WHEN to_char (date_col 'MI') między 0 a 10 THEN TO_DATE (to_char (date_col, 'YYYY') || '-' || TO_CHAR (date_col, 'MM') || '-' || TO_CHAR (date_col, 'DD') || '' || TO_CHAR (date_col, 'HH') || ': 00' "RRRR-MM-DD HH: MI") KONIEC "? –

+0

@OMG Kucyki: Przepraszam, ale to zwraca pełną godzinę, gdy '0 <= MI <= 10', w przeciwnym razie' NULL'. –

+0

Nie chciałeś zagracać z resztą KIEDY –

Odpowiedz

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

nawet

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

@Tom: Witamy w StackOverflow! Oba podejścia wydają się działać, pierwszy wziął "04.18 s", drugi tylko "03.33 s"! Wydajność twojego drugiego zapytania jest prawie taka sama jak w przypadku mojego pierwotnego zapytania, ale unika się 'TO_CHAR', więc przyjmuję twoją odpowiedź. Dzięki! –

1

Możesz pobrać zwróconą wartość jako ciąg i podciąć lewą stronę do ostatniej minuty i zastąpić ją 0. Nie powiedziałbym tego lepiej, chyba że podasz jakiś rodzaj danych.

+1

SELECT SYSDATE, TO_DATE (SUBSTR (TO_CHAR (SYSDATE, 'YYYYMMDD HH24MI'), 1, 12) || '0', 'YYYYMMDD HH24MI') OD DUAL Co powiedział w Kodzie SQL i Co to jest Mówiąc o wydajności, nie jestem pewien, który byłby lepszy, ale twój wygląd byłby prostszy. – Craig

+0

Zobacz test wydajności w edytowanym pytaniu. To podejście przyjęło "04.32 s". –

1

Niekoniecznie lepsza, ale inna metoda:

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

Zobacz test wydajności w edytowanym pytaniu. To podejście zajęło "04.16 s". –

+0

Zaakceptowany, ponieważ jest krótki i czytelny. Dzięki! Zostanie jednak z moim rozwiązaniem, ponieważ zajmuje to mniej czasu. –

+0

@ Tony Andrews: Zaakceptowałem odpowiedź Toma zamiast ciebie, ponieważ uważam, że jest bardziej intuicyjna, a wydajność lepsza. Mam nadzieję, że nie masz nic przeciwko ... –

3

ja generalnie nienawidzę robić datę -> znak -> Data konwersji, gdy nie jest to konieczne. Wolę używać liczb.

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

ten wydobywa się z protokołem z dnia bieżącego, obcina je w dół do przedziału 10 minut, a następnie dodaje je z powrotem do sprawiają, że data ponownie. Oczywiście możesz zastąpić sysdate dowolną datą. Ufa niejawnym konwersjom o wiele więcej niż chcę, ale przynajmniej będzie działać dla dowolnego formatu daty NLS.

+0

Zobacz test wydajności w edytowanym pytaniu. To podejście zajęło "04.39 s". –

1

Inną metodą,

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

alternatywę, która może być szybsze, ponieważ usuwa wezwanie do 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: Inne ciekawe podejście, które działa. W teście wzięło "04.60 s", więc "mod" sprawia, że ​​jest on wolniejszy niż moje podejście. –

+0

Opublikowaliś my alternatywę, która usuwa wezwanie do przyciĘ ... cia. Ciekawie będzie sprawdzić, czy jest jakaś różnica w teście. –

+0

@ar: Niestety, nie otrzymaliśmy powiadomienia o aktualizacji. Twoje zaktualizowane zapytanie działa lepiej: '04.22 s' –

0

Aby powrócić do następnego górnego przedziału 10 minut, to stosuje się następujące zapytania. Mam nadzieję, że będzie to przydatne, ponieważ nie mogę po prostu zrobić

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

Zrobiłem to i pracował dla mnie.

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

ta opiera się na this postu.

0

Myślę, że aby rozwiązać ten problem, istnieje znacznie łatwiejszy i szybszy sposób na zaokrąglenie do następnych 10 sekund, 1 minuty, 10 minut itd. Próbować manipulować timestamp jako ciąg za pomocą substr() tak:

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; 
Powiązane problemy