sql标量函数没有返回正确的值

时间:2014-08-12 21:11:21

标签: sql sql-server sql-server-2012

我编写了一个标量函数来尝试返回一个向下舍入到指定倍数的日期时间。

该功能基于此公式

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

我做错了什么?

1 个答案:

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