我在工作中处理一个查询,它有一个左连接
cast(cola as varchar) + '-' + right('000' + cast(colb as varchar), 3) = x
此查询的实际执行计划相当接近,估计为 269,实际为 475。
将右 +padding 更改为使用 format(colb, '000') 会导致对行数的巨大错误估计,至少减少 400 万,这会导致查询花费 10-15 倍的时间。
我明白为什么错误估计会导致问题,但我不明白为什么使用 Format 会导致估计不太准确。
FORMAT
返回nvarchar
具有比比较的 varchar 列更高的数据类型优先级。除了不精确的行计数估计,比较varchar
列的隐式转换nvarchar
将阻止该列上的索引得到有效使用尝试将
FORMAT
结果转换为varchar
.这里发生了一些事情:
涉及的因素有:
VARCHAR
与数据相比,具有 SQL Server 排序规则的索引列NVARCHAR
(请注意:这种情况是特定于排序规则类型的:如果排序规则是 Windows 排序规则,则不会出现明显的性能下降。有关详细信息,请参阅“对索引的影响”混合 VARCHAR 和 NVARCHAR 类型时" )WITH FULLSCAN
也没有效果)上述三个因素已通过测试得到证实(见下文)。三个因素中的两个很容易纠正:
NVARCHAR
值转换为,或者将列的排序规则更改为 Windows 排序规则。VARCHAR
VARCHAR
REBUILD
的索引。自己做ALTER INDEX ... REORGANIZE;
orUPDATE STATISTICS ... WITH FULLSCAN;
似乎没有帮助(至少在估计的行数方面)。CASE / CONVERT
+RIGHT
比 更有效FORMAT
,并且产生相同的结果,那么一定要使用CASE / CONVERT
+RIGHT
;FORMAT
可以做一些漂亮的事情,但对于左填充是不必要的)。还要记住优先事项。虽然拥有准确的估计行数是理想的,但如果它们足够接近就没问题。意思是,如果这样做不会带来任何真正的性能提升,那么不要觉得需要做额外的工作来获得超准确的估计行数(特别是因为,根据碎片级别,非确定性函数有时有一个更准确的行估计!)。另一方面,更改数据类型(被比较的值)或排序规则是值得的,因为这将产生显着的积极影响。然后,执行
REBUILD
索引将使您足够接近估计的行数。测试方法
我通过填充一个本地临时表来测试这个,其中包含 500 万行的“名称”列
sys.all_objects
(并使用排序规则SQL_Latin1_General_CP1_CI_AS
),然后在字符串列上创建一个非聚集索引,然后添加另外 10 万行来分段索引.我过滤了一个
VARCHAR
文字,然后过滤了相同的字符串文字,但前缀为大写“N”以使其成为NVARCHAR
. 这隔离了比较值数据类型的问题。然后我过滤相同的文字值,但包装在对
FORMAT
. 这隔离了非确定性函数的问题。为了确认函数确定性的行为效果,我创建了两个 SQLCLR 函数,它们只返回传入的值,但一个是确定性的,另一个不是。这清楚地表明问题是确定性的,而不是函数发生的任何其他事情。我使用了 SQLCLR,因为在 T-SQL 中似乎没有执行此操作的等效方法。即使该函数在系统中被标记为确定性的(通过使用 创建 UDF
WITH SCHEMABINDING
),其行为也将反映非确定性函数的行为(我确实对此进行了测试,但没有在下面包含它)。我使用
SET STATISTICS IO, TIME ON;
,并在 SSMS 中选中了“包括实际执行计划”选项。运行第一组测试后,我执行了:
并重新运行测试。对逻辑读取的改进最小,并且估计的行数没有变化。
然后我执行了:
并重新运行测试。估计的行数没有变化。
然后我执行了:
并最终看到逻辑读取和估计行数方面的改进。
然后,我删除了该表,将其
Latin1_General_100_CI_AS_SC
用作排序规则重新创建,然后如上所述重新运行测试。测试代码
SQLCLR代码
以下代码用于创建两个标量函数,它们执行完全相同的操作:简单地返回传入的值。这两个函数之间的唯一区别是一个标记为
IsDeterministic = true
,另一个标记为IsDeterministic = false
。测试设置
测试(和结果)
结果键:
SQL_Latin1_General_CP1_CI_AS
)Latin1_General_100_CI_AS_SC
)REBUILD
} / {之后REBUILD
}第二变奏
FORMAT (Transact-SQL)的备注部分说
因此,查询规划器对该函数的预期结果感到困惑。也许不确定的行为甚至会阻止它应用一些优化,比如缓存中间结果。