【问题标题】:Why is this SQL Function TicksToDateTime non-deterministic? and how to make it deterministic?为什么这个 SQL 函数 TicksToDateTime 是不确定的?以及如何使其具有确定性?
【发布时间】:2020-11-04 21:09:15
【问题描述】:

我有以下函数“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

并在表的计算列中使用它:

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

我现在尝试像这样索引“CallDate”列:

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

但索引失败,因为该列是“非确定性的”,我可以确认该函数也是非确定性的:

select object_id('tickstodatetime')

select OBJECTPROPERTYEX(2127346643, 'IsDeterministic')

返回 false..

所以我的问题是为什么这个函数是“非确定性的”,我如何使它成为确定性的?从我在互联网上阅读的内容来看,它只是说添加“With SchemaBinding”,但正如您所见,我已经添加了它,但它仍然不起作用。

我做错了什么?

【问题讨论】:

  • 尝试使用标准 SQL 日期常量'17530101'
  • @GordonLinoff 我应该在哪里使用 SQL 日期常量?
  • 使用CAST 到/从日期和时间数据类型是不确定的。
  • @Larnu 谢谢你提供了更多信息,所以是否将所有Casts 更改为Converts
  • 我现在正在写一个更具体的答案。

标签: sql sql-server function calculated-columns sql-function


【解决方案1】:

使用CAST 到/从旧日期和时间数据类型是不确定的。使用CONVERT 到/从旧的日期和时间数据类型可以是确定性的,例如,如果使用确定性或不是来自字符串的样式代码。你在这里使用CAST,所以不是。

来自Deterministic and Nondeterministic Functions

以下函数并不总是确定性的,但当它们以确定性方式指定时,可用于索引视图或计算列的索引。

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.

在这种情况下,您需要使用CONVERT 和样式代码。因此,例如,而不是:

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

你需要这样做:

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

【讨论】:

  • 谢谢,有道理。我在最后一行有点难过,使用转换而不是转换转换滴答声。此行CAST((@t - 599266080000000000) / 10000000 / 24 / 60 / 60 AS datetime)
  • bigintdatetimeCONVERT @user1 中是确定性的,如引用的文档所示。
  • 是的..我确实解决了它,我认为它只是Convert(datetime,(@t - 599266080000000000) / 10000000 / 24 / 60 / 60)
【解决方案2】:

就像@Larnu's 优秀答案的扩展:

我最终得到的功能是:

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

并通过验证

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

【讨论】:

  • 仅供参考,'9999121' 应该是 '99991201'
猜你喜欢
  • 2018-07-06
  • 2015-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
  • 2019-06-26
相关资源
最近更新 更多