SQL Server:在SQL Server中创建一个日期,但保持确定性

SQL Server:在SQL Server中创建一个日期,但保持确定性,sql,sql-server,indexing,sql-server-2000,deterministic,Sql,Sql Server,Indexing,Sql Server 2000,Deterministic,(这与以下内容有关。) 是否存在确定性表达式来定义日期时间?当我将其用作计算列公式时: DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0) 当我在该列上放置索引时,出现错误: 无法创建索引,因为键列“EffectiveDate”不确定或不精确 但DATEDIFF和DATEADD从定义上来说都是确定性函数。陷阱在哪里?是否可能?您的列[datetime\u column]是否将默认值设置为“getDate()” 如果是这样,由于getdate()

(这与以下内容有关。)

是否存在确定性表达式来定义日期时间?当我将其用作计算列公式时:

DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0)
当我在该列上放置索引时,出现错误:

无法创建索引,因为键列“EffectiveDate”不确定或不精确


但DATEDIFF和DATEADD从定义上来说都是确定性函数。陷阱在哪里?是否可能?

您的列[datetime\u column]是否将默认值设置为“getDate()”

如果是这样,由于getdate()函数是非确定性的,这将导致此错误

用户定义函数是确定性函数还是非确定性函数取决于函数的编码方式。在以下情况下,用户定义的函数是确定的:

  • 该函数是模式绑定的
  • 所有内置或用户定义的 由用户定义的 函数是确定性的
  • 函数引用的主体 数据库外没有数据库对象 职能范围。例如 确定性函数不能 表以外的参考表 变量的局部变量 功能
  • 该函数不调用任何 扩展存储过程
  • 不符合这些条件的用户定义函数被标记为不确定函数。用户定义函数体中不允许使用内置的非确定性函数。

    请尝试以下操作:

    CAST(FLOOR(CAST([datetime_column] as FLOAT)) AS DateTime)
    
    它应该比CONVERT选项快得多。

    查看。也许解决方案是使用WITH SCHEMABINDING创建一个函数,然后在计算列中使用它

    编辑

    我知道您的目标是能够在该列上建立索引


    如果无法使用计算列完成此操作,那么唯一的选择可能是创建一个普通列,并在每次更新该列所基于的数据时修改该列中的数据。(在触发器中说)

    我建议更简单一些:

     cast(cast([datetime_column] as int) as datetime)
    
    但我怀疑你也会遇到同样的问题


    现在,如果问题在返回到一个DATE时间,您可能需要考虑使用< <代码> CAST([DATEMEMIJAUL]作为INT)< /C>作为单独的字段,仅用于索引。

    < P>我猜这是某种类型的bug。在SQL2005中,我能够毫无问题地创建这样一个索引视图(代码如下)。当我尝试在SQL 2000上运行它时,我得到了与您相同的错误

    以下内容似乎适用于SQL 2000,但我得到一个警告,即索引将被忽略,每次从视图中选择时都必须进行转换

    CONVERT(CHAR(8), datetime_column, 112)
    
    在SQL 2005中工作:

    CREATE TABLE dbo.Test_Determinism (
        datetime_column DATETIME    NOT NULL    DEFAULT GETDATE())
    GO
    
    CREATE VIEW dbo.Test_Determinism_View
    WITH SCHEMABINDING
    AS
        SELECT
            DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0) AS EffectiveDate
        FROM
            dbo.Test_Determinism
    GO
    
    CREATE UNIQUE CLUSTERED INDEX IDX_Test_Determinism_View ON dbo.Test_Determinism_View (EffectiveDate)
    GO
    

    以下是我回答原始问题的最佳答案:

    试试这个:

    /* create a deterministic schema bound function */
    CREATE FUNCTION FloorDate(@dt datetime)
    RETURNS datetime
    WITH SCHEMABINDING
    AS
    BEGIN 
        RETURN CONVERT(datetime,  FLOOR(CONVERT(float, @dt)))
    END
    GO
    
    要进行测试,请尝试以下操作。请注意,计算列使用“persistend”,在引用函数时使用[dbo.]

    /*create a test table */
    CREATE TABLE [dbo].[TableTestFloorDate](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [TestDate] [datetime] NOT NULL,
        [TestFloorDate]  AS ([dbo].[FloorDate]([TestDate])) PERSISTED,
     CONSTRAINT [PK_TableTestFloorDate] PRIMARY KEY CLUSTERED 
    (
        [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
    ) 
    
    您现在应该能够在计算列上添加索引(但请参阅后面的gotcha)

    尽可能多次地插入一些随机数据,但如果您希望测试索引使用/执行计划,则插入更多(1000+)数据更好

    INSERT INTO TableTestFloorDate (TestDate) VALUES( convert(datetime, RAND()*50000))
    
    得到结果

    SELECT * FROM TableTestFloorDate WHERE TestFloorDate='2013-2-2'
    
    现在,我明白了。。。 不使用在计算列上创建的索引!相反,即使在持久化字段TestFloorDate上选择数据,SQLServer(或至少是我的版本)也更喜欢在TestDate上创建索引

    CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestDate)
    
    我(从内存中)非常肯定,从性能的角度来看,计算的、持久化的列上的索引是有益的——我想您只需要尝试/测试自己的特定用法


    (希望我能帮上忙!)

    CAST对于datetime值是不确定的。我认为getDate是不确定的,所以这就是原因,但它只是默认值。这不是公式的一部分。顺便说一句,对于默认的空列,我得到了相同的错误。我猜执行此操作的子例程没有区分列定义中表示公式的部分和表示默认值的部分…是的。我一直在尝试克里斯托夫的建议。问题还在于从datetime(或convert()ing,就这一点而言)开始的铸造。我现在没有办法测试它。这台SQL Server 2000兼容吗(我知道它很古老,但问题中的标记指出了这个版本)?上面的测试是在Sql2012中进行的,但从内存来看,我不明白为什么它不能以上面描述的方式在Sql2k上工作
    CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestDate)