sql Skalar-function nicht wieder korrekten Wert

Ich habe eine Skalar-function geschrieben, um zu versuchen, eine datetime zurückzugeben, die auf ein angegebenes Vielfaches abgerundet wird.

Die function basiert auf dieser Formel

select dateadd(minute, (datediff(minute, 0, '2014-01-05 12:23:05') / 30) * 30, 0) 

das kommt richtig zurück

2014-01-05 12: 00: 00.000

Ich habe versucht, es als eine function zu schreiben:

 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 

aber wenn ich eine select-statement ausführe

 select dbo.roundtime('2014-01-05 12:23:05',30) 

oder auch wenn ich die time in ein datetime Format zwinge, um die Definition zu entsprechen

 select businessusers.dbo.roundtime(cast('2014-01-05 12:23:05' as datetime),30) 

es gibt dies zurück:

2014-01-05 12: 23: 00.000

Was mache ich falsch?

Diese Methode der Rundung beruht darauf, dass die Teilung integral ist, wie Lamak richtig bemerkt hat .

In SQL server ist die Division integral, wenn beide Operanden ganze Zahlen sind.

In deinem ersten Beispiel ist das Ergebnis von DATEDIFF int per Definition und die 30 wird vom Parser als int auch erkannt. Daher funktioniert die Abfrage wie erwartet.

In Ihrer function wird jedoch einer der Operanden der Division @RoundTo als float deklariert. Das schreibt SQL server vor, eine normale Teilung zu verwenden, um zu verhindern, dass eine Rundung stattfindet.

Es gibt grundsätzlich zwei Möglichkeiten, dies zu lösen. Man ist offensichtlich, um die Art von @RoundTo zu einem Integer-Typ zu ändern. (Normalerweise funktioniert das gut, obwohl seine Kapazität für diesen Anwendungsfall zu groß erscheinen kann.)

Die andere Methode, wenn du aus irgendeinem Grund mit dem float haften willst (zB willst du auf die nächstgelegene halbe Minute oder ein solches Intervall runden), wäre es, die FLOOR() function auf das Ergebnis der Division anzuwenden :

 floor( datediff(minute, 0, @Time) / @RoundTo ) * @RoundTo 

Wenn jedoch @RoundTo keine ganze Zahl war, könnte das Ergebnis der Multiplikation mit @RoundTo auch nicht ganz sein. Um die daraus resultierenden Intervalle konsistent zu machen, müssten Sie wahrscheinlich die Minuten in Sekundenschnelle in die letzte Stufe umwandeln:

 dateadd( second , floor(datediff(minute, 0, @Time) / @RoundTo) * @RoundTo * 60 , 0)