SQL Server视图创建索引报错:计算列不精确/非确定性问题求助
解决SQL Server视图计算列InTime的索引创建问题
核心问题分析
要在视图的计算列上创建索引,该列必须满足两个硬性要求:
- 确定性:相同输入下,计算结果始终一致
- 精确性:计算结果为精确数据类型(不能是float这类近似类型)
你之前的两种写法均不符合要求:
- 第一种用
float转换datetime属于近似计算,违反精确性要求 - 第二种通过子查询获取时间段,依赖外部表动态数据,导致计算列非确定性(SCHEDULLE_TABLE数据变更会直接影响结果)
可行解决方案
方案1:重构视图逻辑,消除非确定性子查询
将子查询改为JOIN关联,确保计算逻辑完全基于确定的列关联,同时使用精确时间类型比较:
ALTER VIEW [dbo].[MyView] WITH SCHEMABINDING AS SELECT A.LOCAL_DATE, CASE WHEN CAST(A.LOCAL_DATE AS TIME) BETWEEN CASE WHEN (DATEDIFF(day, '1900-01-01', A.LOCAL_DATE) % 7) = 0 THEN CAST(E.StartDate AS TIME) ELSE CAST(E.StartDateB AS TIME) END AND CASE WHEN (DATEDIFF(day, '1900-01-01', A.LOCAL_DATE) % 7) = 0 THEN CAST(E.EndDate AS TIME) ELSE CAST(E.EndDateB AS TIME) END THEN 1 ELSE 0 END AS InTime FROM dbo.EventTable AS A INNER JOIN dbo.SCHEDULLE_TABLE AS E ON E.IdCen = A.IdCen -- 注意:移除TOP (100),带TOP的视图无法创建索引 GO
方案2:将计算列持久化到基表(推荐)
如果允许修改基表,直接在EventTable中添加持久化计算列,再基于该列创建索引(比视图索引性能更优):
- 添加持久化计算列:
ALTER TABLE dbo.EventTable ADD InTime AS CASE WHEN CAST(LOCAL_DATE AS TIME) BETWEEN CASE WHEN (DATEDIFF(day, '1900-01-01', LOCAL_DATE) % 7) = 0 THEN (SELECT CAST(StartDate AS TIME) FROM dbo.SCHEDULLE_TABLE E WHERE E.IdCen = EventTable.IdCen) ELSE (SELECT CAST(StartDateB AS TIME) FROM dbo.SCHEDULLE_TABLE E WHERE E.IdCen = EventTable.IdCen) END AND CASE WHEN (DATEDIFF(day, '1900-01-01', LOCAL_DATE) % 7) = 0 THEN (SELECT CAST(EndDate AS TIME) FROM dbo.SCHEDULLE_TABLE E WHERE E.IdCen = EventTable.IdCen) ELSE (SELECT CAST(EndDateB AS TIME) FROM dbo.SCHEDULLE_TABLE E WHERE E.IdCen = EventTable.IdCen) END THEN 1 ELSE 0 END PERSISTED
- 在基表列上创建索引:
CREATE INDEX IX_EventTable_InTime ON dbo.EventTable(InTime)
方案3:修复星期几计算的确定性问题
原写法中DATEPART(WEEKDAY)的结果依赖服务器SET DATEFIRST设置,属于非确定性函数。改用DATEDIFF计算星期几(周日=0,不受系统设置影响),确保计算逻辑完全确定。
关键注意事项
- 视图必须带
WITH SCHEMABINDING,且不能包含TOP、DISTINCT等破坏索引可行性的语句 - 计算列不能依赖任何非确定性函数(如
GETDATE()、受系统设置影响的DATEPART(WEEKDAY)) - 避免用float等近似类型做时间比较,改用
TIME、DATETIME2等精确类型
内容的提问来源于stack exchange,提问作者Jack Casas
相关产品推荐
相关产品推荐

