表 <table_name> 中的计算列 'IsWeekend' 无法持久化,因为该列是不确定的

Mar*_*son 3 sql-server determinism

我正在调整 SQL Server 2012 中的查询,该查询由于DATENAME()在列上使用该函数检查数据是周末还是工作日时基数估计不正确而溢出到 tempdb 。由于使用该函数,查询是不可sargable的,并且错误估计了行数(估计1700,实际38000)。

where 子句很简单:

WHERE DATENAME(WEEKDAY, vqc.DateRecorded) NOT IN ('Saturday', 'Sunday')
Run Code Online (Sandbox Code Playgroud)

我希望使用持久计算列,然后对其进行索引:

ALTER TABLE <table_name> ADD IsWeekend AS DATENAME(WeekDay, DateRecorded) PERSISTED;
Run Code Online (Sandbox Code Playgroud)

但是得到错误:

无法保留表中的计算列“IsWeekend”,因为该列是不确定的。

根据BOL,大量 Date 函数无法持久化,因为它们是不确定的。

这是因为结果取决于服务器会话的 LANGUAGE 和 DATEFORMAT 设置。例如,表达式 CONVERT (datetime, '30 listopad 1996', 113) 的结果取决于 LANGUAGE 设置,因为字符串 '30 listopad 1996' 表示不同语言的不同月份。同样,在表达式 DATEADD(mm,3,'2000-12-01') 中,数据库引擎根据 DATEFORMAT 设置解释字符串 '2000-12-01'。

有一个有趣的方法来解决这个问题,该MONTH()函数使用CASE发布在Stack Overflow上的声明,但这对DATENAME由于SET DATEFIRSTLANGUAGE设置不起作用。

除了在我的查询中加入日历表然后从那里过滤周末之外,有没有办法确定日期是否是周末,以便它可以被持久化然后索引?

还是我想把情况复杂化?

Mik*_*son 5

你可以做一个datediffbetween 1900-01-01,这恰好是一个星期一,并检查 modulo7是 a5还是 a 6

create table D
(
  DateRecorded date,
  IsWeekend as cast(case when datediff(day, 0, DateRecorded) % 7 in (5, 6) 
                      then 1 
                      else 0 
                    end as bit) persisted
)
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您不需要保留计算列以在索引中使用它。