T-SQL:舍入到最近的15分钟间隔

时间:2009-05-06 17:36:27

标签: sql sql-server datetime

将HH:MM值舍入到最接近的15分钟间隔的最佳方法是什么?我不追踪秒,所以它们无所谓。

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

等等。这样做是否有优雅的非UDF或Case语句方法?

编辑:以下是我用来获取上述值的SQL:

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

starttimestoptime是SQL datetime s。

16 个答案:

答案 0 :(得分:63)

我目前正在使用dateadd / datediff变体,其日期为零(0)。不需要施法:

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

GETDATE()是你的约会时间。

这将适用于日期至少5500年之前的日期,因为日期已经过期。但是,如果您尝试使用第二个精度,则上面会立即失败。

使用另一个固定日期,如'2009-01-01',或今天的日期(警告,更丑陋的SQL)将解决这个问题。未来的日期也将有效。只要它的时间部分为00:00:00,您就可以在其上建立另一个日期时间。

例如:舍入到最接近的30秒:

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

答案 1 :(得分:27)

这是在How to Round a Time in T-SQL回答的,我认为它应该对你有用。

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)

答案 2 :(得分:20)

我知道这是一篇旧帖,但想分享我的答案。这建立在@hbrowser响应的基础上。这就是我想出来的。这将向上或向下舍入到最近的15分钟。

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

通过内联而不是在用户定义的函数内部执行此逻辑,在大型记录集上,您应该体验到更高的性能。

     

您可以通过将ROUND函数交换为使用FLOORCAST expr AS INT来始终向下舍入或使用CEILING始终向上舍入来更改舍入方式。

您的个人用例将决定您可能需要使用哪种舍入方式。

以下脚本可用于观察不同舍入技术提供的差异:

  

注意:为简化输出,每个结果都已转换为TIME(0),这只是为了简化此特定示例的输出。

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
Time        Rounded     Casted      Floored     Ceilinged
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: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: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    01:00:00
00:55:00    01:00:00    00:45:00    00:45:00    01:00:00
01:00:00    01:00:00    01:00:00    01:00:00    01:00:00
01:05:00    01:00:00    01:00:00    01:00:00    01:15:00

答案 3 :(得分:6)

您可以将日期舍入到最近的一个季度,例如:

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

将datetime转换为double precesion以避免溢出,double = float(53)。乘以24 * 4,即一天中的季度数。使用floor()舍入到最接近的四分之一倍数,然后除以24 * 4以转换回正常时间。

答案 4 :(得分:5)

尝试了Andomar的答案,并且在30和00处出现了四舍五入的问题 - 所以进行了一些调整,这非常有效:

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

这将显示最后15分钟的增量,而不是最近的增量,即它不会前进,这正是我需要的。

答案 5 :(得分:2)

最简单的方法:

将分数转换为十进制数除以60。

8/60 = 0.1333333333333333

乘以4

0.1333333333333333 * 4   = 0.5333333333333333

围绕产品:

Round(0.5333333333333333,0) = 1

将整数除以4

1/4 = 0.25 = 15 minutes

如果您希望会议记录只需将其乘以60

0.25*60 = 15

给一个人一条鱼......

答案 6 :(得分:1)

试试这个:

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

答案 7 :(得分:1)

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)

答案 8 :(得分:1)

- 这是我最喜欢的回合时间

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)

答案 9 :(得分: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,-@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'

答案 10 :(得分:0)

T-SQL中的时间舍入实际上是非常有问题的,并且很多时候都是不准确的。

多年前,我把所有的时间都转移到代码中,而不是使用T-SQL中所有额外的hub-bub来实现它并使其准确发生。代码中的舍入时间更容易,更准确。

如果您遇到T-SQL并且没有支持代码,或者无法访问该代码,请按照前面提到的示例进行操作。否则,我谦虚地建议让代码完成工作。

答案 11 :(得分:0)

这个怎么样? (为了便于阅读而增加了变量)

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

答案 12 :(得分:0)

在15分钟内设置阻止:

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

答案 13 :(得分:0)

这将四舍五入到最接近的15分钟。您可以将@ROUND修改为您选择的时间间隔。

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

答案 14 :(得分:0)

前提分解以确定你想要的增量,以及60分钟的百分比...然后计算出到达那里所需的增量数量......取INT值(这个剁关闭余数)并且你有它,一个简单的函数来向上或向下舍入到最接近的增量。

简单功能:

    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

答案 15 :(得分:0)

    DECLARE   @Date             DATETIME = GETDATE()

    SELECT    @Date
            , DATEADD(ms, 900000 - DATEDIFF(ms, CAST(@Date AS DATE), @Date) % 900000, @Date)