web-dev-qa-db-fra.com

T-SQL: arrondir à l'intervalle de 15 minutes près

Quelle est la meilleure façon d'arrondir une valeur HH: MM à l'intervalle de 15 minutes le plus proche? Je ne surveille pas les secondes, alors elles ne comptent pas.

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

etc. Existe-t-il une méthode élégante, non UDF ou Case, pour le faire?

EDIT: Voici le code SQL que j'utilise pour obtenir les valeurs ci-dessus que je voudrais arrondir:

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

starttime et stoptime sont SQL datetimes.

42
Dzejms

Ceci a été répondu ici Comment arrondir une heure dans T-SQL et je pense que cela devrait fonctionner pour vous.

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)
26
u07ch

J'utilise actuellement une variante dateadd/datiff avec une date zéro (0) pour cela. Aucun casting requis:

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

GETDATE () est ce que votre date-heure est.

Cela fonctionnera pendant au moins l’année 5500 avant l’échec de la date du délai en raison d’un débordement. Cependant, si vous essayez d’utiliser la seconde précision, l’échelle ci-dessus échouera immédiatement. 

L'utilisation d'une autre date fixe, comme «2009-01-01», ou la date du jour (avertissement, SQL plus laid) résoudra ce problème. Une date future fonctionnera également. Tant qu’il a une partie heure de 00:00:00, vous pouvez y baser une autre date/heure.

par exemple: arrondir à 30 secondes près:

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

Je sais que c'est un ancien post, mais je voulais partager ma réponse. Cela s'appuie sur la réponse de @hbrowser. Voici ce que je suis venu avec. Ceci arrondira aux 15 minutes les plus proches.

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

En effectuant cette logique en ligne, plutôt que dans une fonction définie par l'utilisateur, sur des jeux d'enregistrements volumineux, vous obtiendrez de meilleures performances. 

Vous pouvez changer le mode d'arrondi en permutant la fonction ROUND à utiliser FLOOR ou CAST expr AS INT pour toujours arrondir le bas ou utiliser CEILING pour toujours arrondir.

Votre cas d'utilisation individuel déterminera le style d'arrondi que vous devrez peut-être utiliser.

Le script suivant peut être utilisé pour observer les différences offertes par les différentes techniques d'arrondi:

NOTE: pour simplifier la sortie, chaque résultat a été converti en TIME (0). Ceci est fait uniquement pour simplifier la sortie pour cet exemple particulier.

DECLARE @SequenceStart SmallDateTime = CAST(GETDATE() AS Date); 
DECLARE @SequenceEnd SmallDateTime = DateAdd(HOUR, 2, @SequenceStart); -- Recursive CTEs should always have an upper limit
DECLARE @SequenceIntMins INT = 5; -- increment by 5 to show the difference with rounding
WITH TimeSequence([Time]) as
(
    SELECT @SequenceStart as [Time]
    UNION ALL
    SELECT DateAdd(MINUTE, 5, [Time]) FROM TimeSequence 
    WHERE [Time] <= @SequenceEnd
)
    SELECT [Time] = Cast([Time] as TIME(0))
    , Rounded = CAST(DATEADD(MINUTE, ROUND(DATEDIFF(MINUTE, 0, [Time]) / 15.0, 0) * 15, 0) as TIME(0))
    , Casted = CAST(DATEADD(MINUTE, CAST(DATEDIFF(MINUTE, 0, [Time]) / 15.0 AS INT) * 15, 0) as TIME(0))
    , Floored = CAST(DATEADD(MINUTE, FLOOR(DATEDIFF(MINUTE, 0, [Time]) / 15.0) * 15, 0) as TIME(0))
    , Ceilinged = CAST(DATEADD(MINUTE, CEILING(DATEDIFF(MINUTE, 0, [Time]) / 15.0) * 15, 0) as TIME(0))
FROM TimeSequence OPTION ( MaxRecursion 1000);
-- MaxRecursion may be neccessary if you change the interval or end of the sequence
 Temps arrondi Moulé Plancher Plafond Plafond 
 00:00:00 00:00:00 00:00:00 00:00:00 00:00:00 
 00:05:00 00:00:00 00:00 : 00: 00 00:00:00 00:15:00 
 00:10:00 00:15:00 00:00:00 00:00:00 00:15:00 
 00:15:00 00:15:00 00:15:00 00:15:00 00:15:00 
 00:20:00 00:15:00 00:15:00 00:15:00 00:30:00 
00: 25: 00 00:30:00 00:15:00 00:15:00 00:30:00 
 00:30:00 00:30:00 00:30:00 00:30:00 00:00 : 30: 00 
 00:35:00 00:30:00 00:30:00 00:30:00 00:45:00 
 00:40:00 00:45:00 00:30:00 00:30:00 00:45:00 
 00:45:00 00:45:00 00:45:00 00:45:00 00:45:00 
 00:50:00 00:45: 00 00:45:00 00:45:00 1:00:00 
 00:55:00 1:00:00 00:45:00 00:45:00 1:00:00 
 01:00 : 00 01:00:00 01:00:00 01:00:00 01:00:00 
 01:05:00 1:00:00 1:00:00 1:00:00 1:00:00 
16
Jamie Paolino

Vous pouvez arrondir une date au quartier le plus proche comme:

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

Conversion de la date/heure en double précision pour éviter les débordements, double = float (53). Multipliez par 24 * 4, le nombre de trimestres d'une journée. Arrondissez au multiple de trimestres le plus proche avec floor (), puis divisez par 24 * 4 pour revenir au temps normal.

6
Andomar

J'ai essayé la réponse d'Andomar et il y avait des problèmes d'arrondis à 30 et 00 - donc quelques ajustements et cela fonctionne parfaitement:

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

Cela montrera le dernier incrément de 15 minutes, pas le plus proche, c’est-à-dire qu’il ne sera pas avancé, c’est exactement ce dont j'avais besoin.

4
Tazz602

Bon moyen le plus simple: 

convertir les minutes en nombre décimal en divisant par 60.

8/60 = 0.1333333333333333

multiplier par 4

0.1333333333333333 * 4   = 0.5333333333333333

Arrondir le produit: 

Round(0.5333333333333333,0) = 1

diviser le nombre de tours par 4

1/4 = 0.25 = 15 minutes

si vous voulez les minutes, multipliez-les par 60

0.25*60 = 15

Donne un poisson à un homme ....

1
Surendra Dhanpaul

Essaye ça:

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))
1
Charles Bretana

--C'est ma façon préférée d'arrondir le temps

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)
1
Rhift
DECLARE @t time  ='00:51:00.000' 
DECLARE @m  int = DATEPART(MI,@t)%15

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

-- 2012
SELECT DATEADD(mi,IIF(@m >=8,15-@m,-1*@m),@t)
1
MichaelJ
create function RoundQuarterHour
(
    @dt datetime
)
returns datetime
as
begin
    declare @result datetime
    declare @mm int
    set @mm=datepart(minute,@dt)
    set @result = dateadd(minute,-@mm + (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
feihtthief

Pour définir le blocage en 15 minutes:

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
0
Damián Herrera

Cela sera arrondi aux 15 minutes les plus proches. Vous pouvez modifier @ROUND selon l'intervalle de votre choix.

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
0
Jaime Bennett

Arrondir le temps dans T-SQL est en réalité très problématique et souvent imprécis.

Il y a des années, je passais de temps en temps en code plutôt qu'en utilisant tous les efforts supplémentaires que T-SQL avait à faire pour que tout se passe bien et avec précision. Arrondir les temps dans le code est plus facile et beaucoup plus précis.

Si vous êtes bloqué dans T-SQL et n'avez pas de code de prise en charge, ou n'avez pas accès à ce code, suivez les exemples mentionnés précédemment. Sinon, je recommande humblement de laisser le code faire le travail.

0
Boydski

celui-ci, ça va? (variable ajoutée pour la lisibilité)

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

La prémisse se résume à déterminer quel incrément vous voulez, quel pourcentage en 60 minutes ... puis déterminez le nombre d'incréments nécessaires pour y arriver ... prenez la valeur INT (cela coupe les restes) et voilà. C’est une fonction simple pour arrondir l’incrément le plus proche.

Fonction simple: 

    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
0
Thomas Bright