关于SQL Server中标量UDF的危害,已经写了很多文章。随意搜索将返回大量结果。

不过,在某些地方,标量UDF是唯一的选择。

例如:处理XML时:XQuery不能用作计算列定义。 Microsoft记录的一种选择是使用Scalar UDF将XQuery封装在Scalar UDF中,然后在计算列中使用它。

这会产生多种效果,并提供一些解决方法。


查询表时逐行执行
强制对表的所有查询顺序运行

您可以通过逐行执行对函数进行模式绑定,并保留计算的列或对其进行索引。即使没有引用标量UDF,这两种方法都不能防止查询的强制序列化。

是否有已知的方法?

#1 楼

是,如果您:


正在运行SQL Server 2014或更高版本;并且
能够在跟踪标记176处于活动状态的情况下运行查询;并且
计算列为PERSISTED


具体来说,至少需要以下版本:


SQL Server的累积更新2 2016 SP1
SQL Server 2016 RTM的累积更新4
SQL Server 2014 SP2的累积更新6

但要避免错误(2014年以及2016年和2017年的参考)在这些修复程序中引入,而是应用:


SQL Server 2017的累积更新1
SQL Server 2016 SP1的累积更新5
SQL Server 2016的累积更新8 RTM
SQL Server 2014 SP2的累积更新8

跟踪标志作为启动–T选项有效,在使用DBCC TRACEON的全局范围和会话范围内,对于每个查询使用OPTION (QUERYTRACEON)或a计划标志。

跟踪标志176防止持久化计算的列扩展。

编译查询时执行的初始元数据加载会引入所有列,而不仅仅是直接引用的那些列。这使所有计算的列定义都可用于匹配,这通常是一件好事。

作为不幸的副作用,如果已加载(计算的)列之一使用标量用户定义函数,则其即使对于未实际使用计算列的状态,presence也会禁用整个查询的并行性。

跟踪标志176通过保持不加载定义(如果跳过了扩展)来帮助保持列的持久性。 。这样,标量用户定义函数永远不会出现在编译查询树中,因此不会禁用并行性。

跟踪标志176的主要缺点(除了仅作简单记录外)还阻止查询表达式与持久化的计算列匹配:如果查询包含与持久化的计算列匹配的表达式,跟踪标志176将阻止表达式替换为有关计算列的引用。

有关更多详细信息,请参见我的SQLPerformance.com文章“适当的持久计算列”。由于该问题提到了XML,因此可以代替升级使用计算列和标量函数的值,您还可以查看使用选择性XML索引,就像您在“选择性XML索引:一点都不错”中所写。

#2 楼

除了@Paul出色的Yes#1,实际上还有一个Yes#2:


可以追溯到SQL Server 2005,
不需要设置跟踪标志,
不需要计算的列为PERSISTED
(由于不需要跟踪标志176),不会阻止查询表达式与持久化的计算列匹配

唯一的缺点(据我所知)是:


在Azure SQL数据库上不起作用(至少现在还不行,尽管它在Amazon RDS SQL Server和SQL上都可以工作) Linux上的服务器),并且
超出了许多DBA的舒适范围。

该选项是:SQLCLR

是的。 SQLCLR标量UDF的一个很酷的方面是,如果它们不进行任何数据访问(无论用户还是系统),那么它们就不会禁止并行性。这不只是理论或市场营销。尽管目前我没有时间进行详细的撰写,但我已经测试并证明了这一点。

我使用了以下博客文章中的初始设置(希望OP可以认为这是不可靠的来源🙃):

坏主意牛仔裤:多种索引提示

并进行了以下测试:


按原样运行初始查询─⇾并行(按预期)
添加了一个定义为([c2] * [c3])的非持久计算列─⇾并行(按预期)
删除了该计算列,并添加了一个非引用了定义为SCHEMABINDING的T-SQL标量UDF(使用RETURN (@First * @Second);创建)的持久化计算列─⇾否并行(按预期)
删除了T-SQL UDF计算列并添加了一个非持久化计算列,该列引用了定义为IsDeterministic = true的SQLCLR标量UDF(同时尝试了= falsereturn SqlInt32.Multiply(First, Second);)─⇾并行性(woo hoo !!)


因此,尽管SQLCLR并非对每个人都适用,但对于那些非常适合的人/情况/环境,它无疑具有优势。并且,由于涉及到这个特定的问题(使用XQuery的示例),它可以为此工作(并且,根据具体执行的操作,它甚至可能更快一点)。