2009-05-06 2 views
35

Qual è il modo migliore per arrotondare un valore HH: MM all'intervallo di 15 minuti più vicino? Non tengo traccia dei secondi, quindi non hanno importanza.T-SQL: intervallo fino a 15 minuti più vicino

00:08:00 becomes 00:15:00 
00:07:00 becomes 00:00:00 
01:59:00 becomes 02:00:00 

e così via. Esiste un metodo elegante, non UDF o Case per fare questo?

EDIT: Ecco lo SQL che sto usando per ottenere i valori di cui sopra che mi piacerebbe per arrotondare:

CONVERT(CHAR(8), DATEADD(n, SUM(DATEDIFF(n, starttime, stoptime)), 0), 108) 

starttime e stoptime sono SQL datetime s.

+0

Quale tipo di dati avete il valore memorizzato come? – Tomalak

+0

Dai un'occhiata a http://stackoverflow.com/questions/829825/how-to-re-format-datetime-in-sql-with-ceiling-or-flooring-method/829927#829927 – ahains

+1

@Tomalak - È un SOMMA dei datat data DATEDIFFed convertiti in HH: MM: SS. Ho ricostruito tutto da una cazzata che ho trovato online. Aggiornerò la domanda con il mio SQL – Dzejms

risposta

25

Questa è stata risposta qui How to Round a Time in T-SQL e penso che dovrebbe funzionare per voi.

CREATE FUNCTION [dbo].[RoundTime] (@Time datetime, @RoundTo float) RETURNS datetime 
AS 
BEGIN 
    DECLARE @RoundedTime smalldatetime, @Multiplier float 

    SET @Multiplier = 24.0/@RoundTo 

    SET @RoundedTime= ROUND(CAST(CAST(CONVERT(varchar, @Time, 121) AS datetime) AS float) * @Multiplier, 0)/@Multiplier 

    RETURN @RoundedTime 
END 

-- Usage  
SELECT dbo.RoundTime('13:15', 0.5) 
+1

L'ho visto e ho pensato che fosse eccessivo. Ho solo pensato che ci dovrebbe essere un approccio più semplice quando non mi importa di ore o secondi. Voglio solo prendere un numero compreso tra 0 e 60 e arrotondarlo appropriatamente al quarto d'ora.Daremo un'altra occhiata alla risposta precedente. – Dzejms

+0

Beh, non esattamente quello che stavo cercando, ma almeno ora ho una UDF flessibile che risolve questo problema e potenzialmente futuri. – Dzejms

5

È possibile arrotondare una data per il trimestre più vicino come:

cast(floor(cast(getdate() as float(53))*24*4)/(24*4) as datetime) 

Casting datetime a doppia precesion per evitare overflow, doppia = float (53). Moltiplicare per 24 * 4, il numero di trimestri in un giorno. Arrotondare al multiplo più vicino di quarti con floor(), quindi dividere per 24 * 4 per riconvertire in tempo normale.

+4

Solo così siamo chiari, questo non va al trimestre successivo. Il floor() fa sempre l'ultimo quarto che si è appena verificato. Per ottenere il quarto più vicino, dovresti sostituirlo con la funzione round() con una precisione pari a 0. –

0
create function RoundQuarterHour 
(
    @dt datetime 
) 
returns datetime 
as 
begin 
    declare @result datetime 
    declare @mm int 
    set @mm=datepart(minute,@dt) 
    set @result = dateadd(minute,[email protected] + (round(@mm/cast(15 as float),0)*15) , @dt) 

    return @result 
end 
go 


      select dbo.RoundQuarterHour('2009-may-5 20:00') , '00' 
union all select dbo.RoundQuarterHour('2009-may-5 20:01') , '01' 
union all select dbo.RoundQuarterHour('2009-may-5 20:07') , '07' 
union all select dbo.RoundQuarterHour('2009-may-5 20:08') , '08' 
union all select dbo.RoundQuarterHour('2009-may-5 20:22') , '22' 
union all select dbo.RoundQuarterHour('2009-may-5 20:23') , '23' 
union all select dbo.RoundQuarterHour('2009-may-5 20:37') , '37' 
union all select dbo.RoundQuarterHour('2009-may-5 20:38') , '38' 
union all select dbo.RoundQuarterHour('2009-may-5 20:52') , '52' 
union all select dbo.RoundQuarterHour('2009-may-5 20:53') , '53' 
union all select dbo.RoundQuarterHour('2009-may-5 20:59') , '59' 
0

L'arrotondamento del tempo in T-SQL è in realtà molto problematico e molte volte impreciso.

Anni fa, ho spostato tutto il numero di volte in codice contro l'utilizzo di tutto l'hub aggiuntivo che bub deve fare in T-SQL per farlo accadere e per accadere con precisione. I tempi di arrotondamento nel codice sono più semplici e molto più accurati.

Se sei bloccato in T-SQL e non hai codice di supporto, o non hai accesso a quel codice, segui gli esempi citati in precedenza. Altrimenti, consiglio umilmente di lasciare che il codice faccia il lavoro.

4

provato la risposta di Andomar e c'era arrotondamento problemi a 30 e 00 - in modo da un qualche ritocco e questo funziona perfettamente:

cast(round(floor(cast(getdate() as float(53))*24*4)/(24*4),5) as smalldatetime) 

questo mostrerà l'ultimo incremento di 15 minuti, non è il più vicino, vale a dire che ha vinto' andare avanti che è esattamente ciò di cui avevo bisogno.

+0

Esattamente il frammento di codice di cui avevo bisogno –

55

Attualmente sto usando una variante dateadd/datediff con una data zero (0) per questo. Nessuna trasmissione richiesta:

select dateadd(minute, datediff(minute,0,GETDATE())/15 * 15, 0) 

GETDATE() è qualunque sia il vostro datetime.

Questo funzionerà per le date almeno fino all'anno 5500 prima che il datediff fallisca a causa di un overflow. Tuttavia, se provi ad usare la seconda precisione, sopra fallirai subito.

L'utilizzo di un'altra data fissa, come "2009-01-01" o Data di oggi (avviso, più brutto SQL) la risolverà. Funzionerà anche una data futura. Finché ha una parte temporale di 00:00:00 puoi basare un altro datetime su di esso.

ad esempio: intorno alla prossima 30 secondi:

select dateadd(second, round(datediff(second, '2010-01-01', GETDATE())/30.0, 0) * 30, '2010-01-01'); 
+1

Questo round avanti al prossimo punto di 15 minuti? –

+4

Questa è una buona soluzione, migliore di quella accettata (complessità e velocità) –

+4

Il metodo top funziona bene, ma tronca, anziché arrotondare, ad es. seleziona dateadd (minuto, datediff (minuto, 0, '2015-05-22T17: 29: 00')/15 * 15, 0) restituisce 2015-05-22 17: 15: 00.000 –

0

come su questo uno? (Variabili aggiunto per migliorare la leggibilità)

create function dbo.FloorTimeToQuarters 
(
@dt as datetime 
) 
RETURNS datetime 
as 

BEGIN 

DECLARE @timeAsInt bigint 
SET @timeAsInt = (cast(@dt as float) * 96) 
RETURN DateAdd(hour, @timeAsInt % 96, cast(@timeAsInt/96 as datetime) ) 

END 
1

Prova questo:

Declare @Dt DateTime 
Set @Dt = getDate() 

Select DateAdd(minute, 
     15 * ((60 * Datepart(hour, @Dt) + 
     Datepart(Minute, @Dt)+ 
     Case When DatePart(second, @Dt) < 30 
     Then 7 Else 8 End)/15), 
    DateAdd(day, DateDiff(day, 0, @Dt), 0)) 
0

Per impostare blocchi in 15 minuti:

CREATE FUNCTION RoundQuarterHour (
    @dt DATETIME 
) RETURNS DATETIME 

AS 
BEGIN 
    DECLARE @date DATETIME 
    SET @date = CONVERT(varchar(16),@dt,121) --Sin segundos, ni milisegundos 
    RETURN DATEADD(MINUTE,(DATEPART(MINUTE,@date) % 15)*-1, @date) 
END 

PRINT dbo.RoundQuarterHour('2011/01/01 18:00:07') --Jan 1 2011 6:00PM 
PRINT dbo.RoundQuarterHour('2011/01/01 18:01:07') --Jan 1 2011 6:00PM 
PRINT dbo.RoundQuarterHour('2011/01/01 18:13:07') --Jan 1 2011 6:00PM 
PRINT dbo.RoundQuarterHour('2011/01/01 18:14:07') --Jan 1 2011 6:00PM 
PRINT dbo.RoundQuarterHour('2011/01/01 18:15:07') --Jan 1 2011 6:15PM 
PRINT dbo.RoundQuarterHour('2011/01/01 18:16:07') --Jan 1 2011 6:15PM 
1
DECLARE @t time ='00:51:00.000' 
DECLARE @m int = DATEPART(MI,@t)%15 

-- 2008 
SELECT DATEADD(mi,CASE WHEN @m >=8 THEN [email protected] ELSE -1*@m END,@t) 

-- 2012 
SELECT DATEADD(mi,IIF(@m >=8,[email protected],-1*@m),@t) 
1

Ok modo più semplice:

convertire il minuti per un numero decimale per divisione g da 60.

8/60 = 0.1333333333333333 

moltiplicare per 4

0.1333333333333333 * 4 = 0.5333333333333333 

Rotonda del prodotto:

Round(0.5333333333333333,0) = 1 

divide il numero di round da 4

1/4 = 0.25 = 15 minutes 

se si desidera che i minuti basta moltiplicarlo per 60

0.25*60 = 15 

Dare un uomo un pesce ....

+0

Utilizzo questo metodo da anni, funziona perfettamente per me ... –

0

--Questo è il mio modo preferito di tempo di andata

DECLARE @Time DATETIME = GETDATE() 
     ,@RoundInterval INT = 30 --in minutes, needs to be a number that can be divided evenly into 60 
     ,@RoundDirection INT = 2 --0 is down to the last interval, 1 is to the nearest interval, 2 is up to the next interval 

SELECT DATEADD(MINUTE,DATEDIFF(MINUTE,0,DATEADD(SECOND,30*@RoundDirection*@RoundInterval,@Time))/@RoundInterval*@RoundInterval,0) 
0

Questo sarà rotonda con l'approssimazione di 15 minuti. Puoi modificare @ROUND nell'intervallo che hai scelto.

Declare @Dt DateTime = '2016-01-01 14:38:00' 
DECLARE @ROUND int = 15; 
SELECT 
CASE WHEN (DATEPART(MINUTE, @Dt) % @ROUND) * 60 + DATEPART(SECOND, @Dt) < (30 * @ROUND) 
THEN DATEADD(minute, datediff(minute,0, @Dt)/@ROUND * @ROUND, 0) 
ELSE DATEADD(minute, (DATEDIFF(minute,0, @Dt)/@ROUND * @ROUND) + @ROUND, 0) 
END 
11

So che questo è un vecchio post ma voleva condividere la mia risposta. Questo si basa sulla risposta di @hbrowser. Ecco cosa mi è venuto in mente. Questo verrà arrotondato per eccesso o per difetto ai 15 minuti più vicini.

SELECT DATEADD(MINUTE, ROUND(DATEDIFF(MINUTE, 0, GETDATE())/15.0, 0) * 15, 0); 
+0

Penso che dovresti specificare esplicitamente nella tua risposta ciò che lo rende _differente_ da [@ hbrowser's] (http: // stackoverflow. com/a/1624984/241211) (cioè il tuo usa 'ROUND', mentre il suo/a fa un' FLOOR' implicito con divisione intera). – Michael

0

Premessa rompe a capire che cosa si vuole incrementare, che cosa è che come percentuale di 60 minuti ... quindi capire il numero necessario di incrementi per arrivarci ... prendere il valore INT (questo braciole off remainders) ed ecco fatto, una semplice funzione per arrotondare in su o in giù fino all'incremento più vicino.

funzione semplice:

ALTER FUNCTION [dbo].[RoundOffDateTime] 
(
    @IncDate DATETIME, 
    @Increment INT 
) 
RETURNS SMALLDATETIME 
AS 
BEGIN 

    DECLARE @IncrementPercent DECIMAL(2,2) = CAST(@Increment as decimal)/60 
    DECLARE @IncMinutes REAL = ROUND(CAST(DATEPART(mi,@IncDate) as decimal)/CAST(@Increment as decimal),0) 
    DECLARE @MinutesNeeded INT = CAST(@IncMinutes * @Increment as INT) 

    RETURN CAST(DATEADD(mi,@MinutesNeeded,DATEADD(ss,-DATEPART(ss,@IncDate),DATEADD(mi,-DATEPART(mi,@IncDate),@IncDate))) as smalldatetime) 

END