在SQL Server中,我有一个基于CLR集成的表值函数GetArchiveImages。我称之为:
SELECT ...
FROM Items
CROSS APPLY GetArchiveImages(Items.ID) AS archiveimages
WHERE ...
问题是每次调用该函数都会产生开销。
如果它可以同时与整个表连接,那么开销就会很小,但由于每行调用一次,所以开销与行数一致。
我不使用存储过程,因为存储过程返回的表不能与任何东西连接(据我所知)。
是否有一种有效的方法将表与存储过程或函数的结果批量连接,而不是逐行连接?
答案 0 :(得分:2)
由于GetArchiveImages的结果取决于Items.ID,SQL Server必须为每个项目调用该函数,否则您将无法获得正确的结果。
SQL Server可以“分解”的唯一功能是T-SQL内联表值函数。因此,如果您可以将CLR重写为ITVF,您将获得更好的性能。
根据我的经验,调用CLR函数的过多并不是那么大。您更有可能在查询中的其他位置遇到问题。例如,SQL Server不知道该函数将返回多少行,并假设它将是一个(对于每个调用)。在优化过程中,这可能导致其他地方的错误决策。
更新:
SQL Server不允许在CLR类中保留静态非常量数据。有办法欺骗系统,例如通过创建静态最终集合对象(您可以添加和删除静态集合中的项目),但是,出于稳定性原因,我建议不要这样做。
在您的情况下创建一个缓存表可能是有意义的,该缓存表可以使用某种(数据库或文件系统)触发器或按计划自动刷新。您可以加入该表,而不是调用该函数。
答案 1 :(得分:1)
如果GetArchiveImages()
函数不需要在多个查询中使用,或者至少不在类似查询之外使用,则可以切换此外部和内部方面:执行主SELECT fields FROM [Items] WHERE ...
在SQLCLR TVF中。并使其成为流媒体TVF。
所需的基本结构是:
将SqlDataRecord类型的变量定义为要从[Items]返回的所有字段以及当前GetArchiveImages()
函数返回的其他字段。
阅读"文件系统中的几个文件" (摘自@Sebastian Meine的第一条评论&#39>
使用"Trusted_Connection = true; Enlist = false;"
作为ConnectionString打开SqlConnection。
执行主SELECT fields FROM [Items] {optional WHERE}
。如果此时可以缩小部分行,请填写WHERE
。您甚至可以将值传递给函数以传递给WHERE子句。
循环浏览SqlDataRecord
:
SqlDataRecord
变量GetArchiveImages()
[Items].[ItemID]
功能获取的相关项目
yield return;
关闭SqlConnection
弃置SqlDataReader
,SqlCommand
和SqlConnection
。
关闭在步骤2中打开的所有文件(如果在此过程中无法关闭它们)。