JKennedy
JKennedy

Reputation: 18799

Why is this SQL Function TicksToDateTime non-deterministic? and how to make it deterministic?

I have the following function "TicksToDateTime"

CREATE FUNCTION [dbo].[TicksToDateTime] (@t bigint)
RETURNS datetime
WITH SCHEMABINDING
AS
-- converts the given datetime to .NET-compatible ticks
-- see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdatetimeclasstickstopic.asp
BEGIN 
    declare @result datetime
    if (@t=0)
        set @result = null
    else
        if (@t < 552877919999983334)
            set @result = cast ('1753-1-1' as datetime)
        else
            if (@t=3155378975999999999)
                set @result = cast ('9999-12-1' as datetime)
            else
                set @result = CAST((@t - 599266080000000000) / 10000000 / 24 / 60 / 60 AS datetime)
    return @result
END
GO

and the use it in a computed column of a table:

[CallDateRaw] BIGINT NOT NULL,
[CallDate] AS ([dbo].[TicksToDateTime]([CallDateRaw])),

I am now trying to index the "CallDate" column like so:

Create Index ExternalCalls_CallDate2 ON [External.Call] (CallDate)
GO

But the index fails because the column is "non-deterministic" and I can confirm that the function is also non-deterministic with:

select object_id('tickstodatetime')

select OBJECTPROPERTYEX(2127346643, 'IsDeterministic')

Which returns false..

So my question is why is this function "non-deterministic" and how do I make it deterministic? from what I read on the internet it just said add "With SchemaBinding" but as you can see I've added that and it still doesnt work.

What am I doing wrong?

Upvotes: 2

Views: 251

Answers (2)

JKennedy
JKennedy

Reputation: 18799

Just as an extension to @Larnu's excellent answer:

the function I ended up with was:

Create FUNCTION [dbo].[TicksToDateTime] (@t bigint)
RETURNS datetime
WITH SCHEMABINDING
AS
-- converts the given datetime to .NET-compatible ticks
-- see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdatetimeclasstickstopic.asp
BEGIN 
    declare @result datetime
    if (@t=0)
        set @result = null
    else
        if (@t < 552877919999983334)
            set @result = Convert(datetime,'17530101',112)
        else
            if (@t=3155378975999999999)
                set @result = Convert(datetime,'99991201',112)
            else
                set @result = Convert(datetime,(@t - 599266080000000000) / 10000000 / 24 / 60 / 60)
    return @result
END


GO

and verified through

select object_id('TicksToDateTime')
select OBJECTPROPERTYEX(11863109, 'IsDeterministic')

Upvotes: 0

Thom A
Thom A

Reputation: 95914

Using CAST to/from the old date and time data types is not deterministic. Using CONVERT to/from the old date and time data types can be deterministic, for example if using a style code that is deterministic or not from a character string. You are using CAST here, so it isn't.

From Deterministic and Nondeterministic Functions:

The following functions are not always deterministic, but can be used in indexed views or indexes on computed columns when they are specified in a deterministic manner.

Function                   Comments
all aggregate functions    All aggregate functions are deterministic unless they are specified with the    OVER and ORDER BY clauses. For a list of these functions, see Aggregate Functions (Transact-SQL).

CAST                       Deterministic unless used with datetime, smalldatetime, or sql_variant.

CONVERT                    Deterministic unless one of these conditions exists:
                           Source type is sql_variant.
                           Target type is sql_variant and its source type is nondeterministic.
                           Source or target type is datetime or smalldatetime, the other source or target type is a character string, and a nondeterministic style is specified. To be deterministic, the style parameter must be a constant. 
                           Additionally, styles less than or equal to 100 are nondeterministic, except for styles 20 and 21. Styles greater than 100 are deterministic, except for styles 106, 107, 109 and 113.

In this case, you need to use CONVERT and a style code. So for example, instead of:

set @result = cast ('1753-1-1' as datetime)

You would need to do:

SET @Result = CONVERT(datetime, '17530101',112);

Upvotes: 5

Related Questions