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 datetime
s.
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)
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');
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
à utiliserFLOOR
ouCAST expr AS INT
pour toujours arrondir le bas ou utiliserCEILING
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
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.
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.
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 ....
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))
--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)
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)
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'
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
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
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.
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
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