获取给定日期列的WeekEnding(Sun)的确定性方法(用于计算列)

时间:2014-12-30 13:35:16

标签: sql-server database sql-server-2008 tsql calculated-columns

使用以下SQL表(在此处小提琴:http://sqlfiddle.com/#!3/578bc/1/0):

CREATE TABLE [dbo].[tbl_DateExample] (
    [ID] [INT] IDENTITY(1, 1) NOT NULL ,
    [BookingDate] [DATE] NOT NULL ,
    [WeekEnding] AS ( DATEADD(DAY, ( 6 ) - ( ( DATEPART(WEEKDAY, [BookingDate]) + @@datefirst ) - ( 2 ) ) % ( 7 ), [BookingDate]) ) ,
    CONSTRAINT [PK_tbl_DateExample] PRIMARY KEY CLUSTERED ( [ID] ASC )
)

请注意它使用计算列,此计算列计算BookingDate列中任何日期的WeekEnding(星期日)日期,而不管@@datefirst环境变量 - 此列必须仅包含星期日。

这在生产中效果很好,但我们需要对此计算列应用索引,为此我们需要将其作为持久计算列,遗憾的是我们使用@@datefirst使其成为非确定性的防止持久性和索引。

我们如何重新创建此列数据(即,在BookingDate列中的任何日期的WeekEnding(星期日)),但使其成为确定性的&准备索引?

PS:我宁愿避免使用触发器,因为生产表已经是一个极其复杂的高流量表,其中包含数百万行&已有几个触发器。

编辑:使用的最终代码:

DATEADD(DAY, 6 - DATEDIFF(DAY, CONVERT(DATE, '01/01/1990', 103), BookingDate) % 7, BookingDate)

1 个答案:

答案 0 :(得分:2)

一种丑陋但可靠的解决方案是,依赖于固定的日期值,然后使用模7的日期差作为DATEPART函数的替代:

DATEPART(WEEKDAY, [BookingDate])  -- with @@datefirst = 1

相当于:

DATEDIFF(DAY, CONVERT(DATETIME,'1990-01-01',120), [BookingDate]) % 7 + 1  -- because 1990-01-01 is a monday

所以请使用以下内容。 注意:我还没有对此进行过测试,所以你应该在[WeekEnding]计算中调整偏移值,直到你做对了:

CREATE TABLE [dbo].[tbl_DateExample] (
    [ID] [INT] IDENTITY(1, 1) NOT NULL ,
    [BookingDate] [DATE] NOT NULL ,
    [WeekEnding] AS ( DATEADD(DAY, ( 6 ) - (DATEDIFF(DAY, CONVERT(DATETIME,'1990-01-01 00:00:00.000',120), [BookingDate]) % 7), [BookingDate]) ) ,
    CONSTRAINT [PK_tbl_DateExample] PRIMARY KEY CLUSTERED ( [ID] ASC )
)

此外,如果您在1990-01-01之前有BookingDate个值,则可能会遇到DATEDIFF返回负值的问题。在这种情况下,请调整固定日期值,以确保它位于任何BookingDate值之前。