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

Dze*_*jms 45 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 
Run Code Online (Sandbox Code Playgroud)

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

编辑:这是我用来获取我想要舍入的上述值的SQL:

CONVERT(CHAR(8), DATEADD(n, SUM(DATEDIFF(n, starttime, stoptime)), 0), 108)
Run Code Online (Sandbox Code Playgroud)

starttime并且stoptime是SQL datetime.

小智 64

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

select dateadd(minute, datediff(minute,0,GETDATE()) / 15 * 15, 0)
Run Code Online (Sandbox Code Playgroud)

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');
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案,比公认的解决方案更好(复杂性和速度方面) (5认同)
  • top方法运行良好,但它截断,而不是舍入,例如选择dateadd(分钟,datediff(分钟,0,'2015-05-22T17:29:00')/ 15*15,0)返回2015-05- 22 17:15:00.000 (4认同)
  • Dito @StephenOberauer,** 警告此解决方案截断 ** 这不会四舍五入到最接近的 15 分钟,而是将其截断到下一个最低的 15 分钟间隔。 (2认同)

u07*_*7ch 27

这里回答了如何在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)
Run Code Online (Sandbox Code Playgroud)


小智 25

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

SELECT DATEADD(MINUTE, ROUND(DATEDIFF(MINUTE, 0, GETDATE()) / 15.0, 0) * 15, 0);
Run Code Online (Sandbox Code Playgroud)

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

您可以通过交换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
Run Code Online (Sandbox Code Playgroud)
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


And*_*mar 6

您可以将日期舍入到最近的区域,如:

cast(floor(cast(getdate() as float(53))*24*4)/(24*4) as datetime)
Run Code Online (Sandbox Code Playgroud)

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

  • 我们很清楚,这并不是最接近的一个季度.floor()总是使它成为刚刚发生的最后一个季度.为了得到最接近的四分之一,你必须用round()函数替换它,精度为0. (4认同)

小智 5

尝试了Andomar的答案,在30和00时出现了四舍五入的问题-进行了一些调整,效果很好:

cast(round(floor(cast(getdate() as float(53))*24*4)/(24*4),5) as smalldatetime)
Run Code Online (Sandbox Code Playgroud)

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