3

在 SQL Server 中,我有一个基于 CLR 集成的表值函数 GetArchiveImages。我这样称呼它:

SELECT ...
FROM Items
CROSS APPLY GetArchiveImages(Items.ID) AS archiveimages
WHERE ...

问题是对函数的每个单独调用都有开销。

如果它可以一次与整个表连接,开销将非常小,但由于它为每一行调用一次,所以开销会随着行数的增加而增加。

我不使用存储过程,因为存储过程返回的表不能与任何东西连接(据我所知)。

有没有一种有效的方法将表与存储过程或函数的结果批量连接,而不是逐行连接?

4

2 回答 2

3

由于 GetArchiveImages 的结果取决于 Items.ID SQL Server 必须为每个项目调用该函数,否则您将无法获得正确的结果。

SQL Server 唯一可以“分解”的函数是 T-SQL 内联表值函数。因此,如果您可以将 CLR 重写为 ITVF,您将获得更好的性能。

以我的经验,调用 CLR 函数的开销并没有那么大。您更有可能在查询的其他地方遇到问题。例如,SQL Server 不知道该函数将返回多少行,只是假设它是一(对于每个调用)。这可能会导致在优化过程中其他地方做出错误的决策。


更新:

SQL Server 不允许在 CLR 类中保留静态非常量数据。有一些方法可以欺骗系统,例如通过创建静态最终集合对象(您可以从静态集合中添加和删除项目),但是,出于稳定性原因,我建议不要这样做。

在您的情况下,创建一个使用某种(数据库或文件系统)触发器或按计划自动刷新的缓存表可能是有意义的。您可以直接加入该表,而不是调用该函数。

于 2013-01-15T15:24:31.017 回答
2

如果该GetArchiveImages()函数不需要在多个查询中使用,或者至少不需要在类似查询之外使用,则可以在此切换 Outer 和 Inner 方面:SELECT fields FROM [Items] WHERE ...在 SQLCLR TVF 中做 main。并使其成为流媒体 TVF。

所需的基本结构是:

  1. 将 SqlDataRecord 类型的变量定义为要从 [Items] 返回的所有字段以及当前GetArchiveImages()函数返回的其他字段。

  2. 阅读“文件系统中的几个文件”(取自@Sebastian Meine 回答的第一条评论)

  3. "Trusted_Connection = true; Enlist = false;"使用ConnectionString打开一个 SqlConnection 。

  4. 执行主程序SELECT fields FROM [Items] {optional WHERE}。如果此时可以缩小某些行的范围,请填写WHERE. 您甚至可以将值传递给函数以传递给 WHERE 子句。

  5. 循环遍历SqlDataRecord

    1. 填写SqlDataRecord该行的变量
    2. 获取当前GetArchiveImages()函数正在获取的相关项[Items].[ItemID]
    3. 称呼yield return;
  6. 关上SqlConnection

  7. 处理SqlDataReaderSqlCommandSqlConnection

  8. 关闭在步骤 2 中打开的所有文件(如果无法在此过程的早期关闭它们)。

于 2015-01-31T20:01:08.797 回答