我编写了一个标量函数来尝试返回一个向下舍入到指定倍数的日期时间。
该功能基于此公式
select dateadd(minute, (datediff(minute, 0, '2014-01-05 12:23:05') / 30) * 30, 0)
正确返回
2014-01-05 12:00:00.000
我试着把它写成一个函数:
CREATE FUNCTION [dbo].[RoundTime] (@Time datetime, @RoundTo float) RETURNS datetime
AS
BEGIN
DECLARE @RoundedTime datetime
SET @RoundedTime = dateadd(minute, (datediff(minute, 0, @Time) / @RoundTo) * @RoundTo, 0)
RETURN @RoundedTime
END
但是当我运行一个select语句时
select dbo.roundtime('2014-01-05 12:23:05',30)
甚至当我强制将时间转换为日期时间格式以匹配定义时
select businessusers.dbo.roundtime(cast('2014-01-05 12:23:05' as datetime),30)
它返回:
2014-01-05 12:23:00.000
我做错了什么?
答案 0 :(得分:2)
这种舍入方法依赖于除法积分as Lamak has correctly noted。
在SQL Server中,当两个操作数都是整数时,除法是不可或缺的。
在您的第一个示例中,DATEDIFF
的结果定义为int
,而30
也被解析器识别为int
。因此,查询按预期工作。
但是,在您的函数中,除法的一个操作数@RoundTo
被声明为float
。这规定SQL Server使用正常除法,防止任何舍入发生。
基本上有两种解决方法。显然,一个是将@RoundTo
的类型更改为整数类型。 (通常int
工作正常,即使它的容量对于这个用例来说可能看起来太大了。)
另一种方法,如果由于某种原因你想坚持使用float
(例如你希望能够向下舍入到最接近的半分钟或某个这样的间隔),那就是应用{ {3}}对除法的结果起作用:
floor(datediff(minute, 0, @Time) / @RoundTo) * @RoundTo
但是,如果@RoundTo
不是整数,则乘以@RoundTo
的结果可能也不完整。为了使得到的间隔保持一致,您可能需要在最后阶段将分钟转换为秒:
dateadd(second, floor(datediff(minute, 0, @Time) / @RoundTo) * @RoundTo * 60, 0)