我能够使用您提供的项目重现问题(可能与
这里和连接项
这里相同)。
计算列首先展开为底层表达式,然后可能会再次匹配到计算列。
您的计划中的过滤器显示它被展开为。
CONVERT(date,[computed-column-index-problem].[dbo].[toTimeZone](CONVERT_IMPLICIT(datetime,[computed-column-index-problem].[dbo].[t].[c2],0),CONVERT_IMPLICIT(nvarchar(max),[computed-column-index-problem].[dbo].[t].[c3],0),CONVERT_IMPLICIT(nvarchar(max),'UTC',0)),0)>=CONVERT_IMPLICIT(date,[@1],0)
AND
CONVERT(date,[computed-column-index-problem].[dbo].[toTimeZone](CONVERT_IMPLICIT(datetime,[computed-column-index-problem].[dbo].[t].[c2],0),CONVERT_IMPLICIT(nvarchar(max),[computed-column-index-problem].[dbo].[t].[c3],0),CONVERT_IMPLICIT(nvarchar(max),'UTC',0)),0)<=CONVERT_IMPLICIT(date,[@2],0)
这些对于
nvarchar(max)
的隐式转换似乎是造成问题的原因。一个不需要 CLR 的简单重现代码如下:
DROP TABLE IF EXISTS t
DROP FUNCTION IF EXISTS [dbo].[toTimeZone]
GO
CREATE FUNCTION [dbo].[toTimeZone] (@newTimeZone [NVARCHAR](max))
RETURNS DATE
WITH schemabinding
AS
BEGIN
RETURN DATEFROMPARTS(1970, 01, 02)
END
GO
CREATE TABLE t
(
c1 INT IDENTITY PRIMARY KEY,
c4 AS dbo.toTimeZone(N'UTC') persisted
);
CREATE INDEX i
ON t (c4);
INSERT INTO t
DEFAULT VALUES
SELECT c1
FROM t WITH (forceseek)
WHERE c4 >= '1970-01-02'
AND c4 <= '1970-03-04';
错误 8622,级别 16,状态 1,行 27:查询处理器无法生成查询计划,因为该查询中定义了提示。请重新提交查询,不要指定任何提示并且不要使用 SET FORCEPLAN。
如果我更改函数定义为
public static DateTime toTimeZone(DateTime dateTime,
[SqlFacet(IsFixedLength=false, IsNullable=true, MaxSize=50)]
string originalTimeZone,
[SqlFacet(IsFixedLength=false, IsNullable=true, MaxSize=50)]
string newTimeZone)
{
return dateTime.AddHours(-8);
}
因此,字符串参数变为
nvarchar(50)
。这样就能匹配并进行查找。
![enter image description here](https://istack.dev59.com/n2qRb.webp)
具体来说,需要传递字面值
UTC
的是第二个参数。如果仅将注释应用于第一个参数,则即使使用
with (forceseek)
提示,计划也不会产生查找。如果仅将注释应用于第二个参数,则可以产生查找 - 尽管计划显示警告。
![enter image description here](https://istack.dev59.com/ClSEq.webp)
to_time_zone
,因此其他人能够在自己的系统上复制这个过程的机会现在为零。 - Damien_The_Unbelieveri
索引。当你运行测试时,统计数据已经过时,优化器做出了错误的决定。如果在表填充数据之后重建索引(或创建索引),那么它的统计数据将是最新的,优化器会做出正确的决定。 - Vladimir Baranov