risposte Sebas' di Justin e possono essere estese con un LEFT JOIN per eliminare 'buchi', che è spesso desiderabile.
Se questo non è necessario, in alternativa, possiamo andare vecchia scuola Oracle DATA aritmetica ...
SELECT TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400 AS time
, AVG(t.value) AS avg_value
FROM foo t
WHERE t.time IS NOT NULL
GROUP BY TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400
ORDER BY TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400
Diamo disfare che un po '. Possiamo separare i componenti di data e ora, utilizzando TRUNC per ottenere la parte di data e utilizzando un TO_CHAR per restituire il numero di secondi da mezzanotte. Sappiamo che 5 minuti sono 300 secondi e sappiamo che ci sono 86400 secondi in un giorno. Quindi possiamo dividere il numero di secondi per 300, e prendere il FLOOR di quello (solo la parte intera), che ci arrotonda al limite di 5 minuti più vicino. Moltiplichiamo quello indietro (per 300), per ottenere di nuovo secondi, e poi lo dividiamo per il numero di secondi in un giorno (86400), e possiamo aggiungerlo nuovamente alla porzione di data (troncata).
Doloroso, sì. Ma incredibilmente veloce.
NOTA: questo restituisce il valore di tempo arrotondato come DATE
, questo potrebbe essere restituito a un timestamp se necessario, ma per i confini anche di 5 minuti, uno DATE
ha una risoluzione sufficiente.
Come un vantaggio di questo approccio, per un grande tavolo, siamo in grado di migliorare le prestazioni della query con l'aggiunta di un indice di copertura per questa query:
CREATE INDEX foo_FBX1
ON foo (TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400,value);
APPENDICE:
MiMo ha fornito una risposta per SQL Server, suggerendo che sarebbe adattabile per Oracle. Ecco un adattamento di tale approccio in Oracle. Si noti che Oracle non fornisce equivalenti per le funzioni DATEDIFF e DATEADD. Oracle utilizza invece l'aritmetica semplice.
SELECT TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
AS time
, AVG(t.value) AS avg_value
FROM foo t
WHERE t.time IS NOT NULL
GROUP BY TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
ORDER BY TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
La scelta del 1 gennaio, 0001 dC come data di base è arbitraria, ma non ho voglia di pasticciare con valori negativi, e cercare di capire se PIANO sarebbe giusto, o se ci sarebbe bisogno di usare CEIL con numeri negativi. (Il numero magico 288 è un risultato di 1440 minuti in un giorno diviso per 5). In questo caso, stiamo prendendo il giorno frazionario, moltiplicando per 1440 e dividendo per 5, e prendendo la parte intera di quello, e quindi rimettendola in giorni frazionari.
Si è tentati di estrarre quella "data di base" da un pacchetto PL/SQL o di ottenerla da una sottoquery, ma l'esecuzione di una di queste potrebbe impedire che questa espressione sia deterministica. E ci piacerebbe davvero mantenere aperta l'opzione di creare un indice basato su funzioni.
La mia preferenza è evitare la necessità di includere una "data base" nel calcolo.
Oracle versione 10g +? – Sebas
Sì, mi dispiace - 10g – Nick