SQL Server:在SQL Server中设置日期,但保持确定性

时间:2008-11-21 17:17:34

标签: sql sql-server indexing sql-server-2000 deterministic

(这与Floor a date in SQL server有关。)

DATETIME是否存在确定性表达式?当我将其用作计算列公式时:

DATEADD(dd, DATEDIFF(dd, 0, [datetime_column]), 0)

当我在该列上放置索引时出现错误:

  

无法创建索引,因为键列'EffectiveDate'是非确定性的或不精确的。

但根据定义,DATEDIFF和DATEADD都是确定性函数。捕获量在哪里?有可能吗?

6 个答案:

答案 0 :(得分:3)

我的猜测是,这是某种错误。在SQL 2005中,我能够创建这样的索引视图而没有问题(代码如下)。当我尝试在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

答案 1 :(得分:2)

您的列[datetime_column]是否将默认值设置为“getDate()”?

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

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

  1. 该函数是架构绑定的。
  2. 所有内置或用户定义 由用户定义的函数调用 功能是确定性的。
  3. 函数体引用 没有数据库对象 功能范围。例如, 确定性功能不能 除表之外的引用表 变量是本地的变量 功能
  4. 该功能不会调用任何功能 扩展存储过程。
  5. 不符合这些条件的用户定义函数被标记为不确定。用户定义的函数体中不允许内置的非确定性函数。

答案 2 :(得分:1)

试试这个:

CAST(FLOOR(CAST([datetime_column] as FLOAT)) AS DateTime)

它应该比CONVERT选项快得多。

答案 3 :(得分:1)

这是回答原始问题的最佳答案:

试试这个:

/* 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

要进行测试,请尝试以下操作。请注意在计算列中使用“PERSISTED”以及在引用函数时使用[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) 
) 

您现在应该能够在计算列上添加索引(但稍后会看到问题)

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestFloorDate)

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

INSERT INTO TableTestFloorDate (TestDate) VALUES( convert(datetime, RAND()*50000))

获取结果

SELECT * FROM TableTestFloorDate WHERE TestFloorDate='2013-2-2'

现在这里是GOTCHA ...... 不使用在计算列上创建的索引!相反,即使在持久化字段TestFloorDate上选择数据,SQLServer(或至少我的版本)也更喜欢TestDate上的索引。

CREATE INDEX IX_TestFloorDate ON  [dbo].[TableTestFloorDate](TestDate)

我很确定(从内存中)计算的,持久化的列上的索引可以从性能角度受益 - 我想你只需要尝试/测试你自己的特定用法

(希望我帮助过!)

答案 4 :(得分:0)

看看that question asked and answered by Cade Roux。也许解决方案是使用WITH SCHEMABINDING创建一个函数,然后在计算列

中使用它

修改

我知道您的目标是能够在该列上拥有索引。

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

答案 5 :(得分:0)

我建议稍微简化:

 cast(cast([datetime_column] as int) as datetime)

但我怀疑你会遇到同样的问题。

现在,如果问题是回到日期时间,您可能要考虑仅将cast([datetime_column] as int)用作单独的字段,仅用于索引。