用于查询的 CLR SQL 与 T-SQL

gil*_*hal 3 sql-server t-sql azure-sql-database sql-clr

我知道 CLR 存储过程在过程代码中更有效,而 T-SQL 更适合查询 - 但是我很难决定使用什么来满足我的特定需求,我想知道是否还有其他事情我在做决定时应该考虑一下。

我计划创建的代码涉及查询一组数据、实现一些算法(随着时间的推移会变得更加复杂)以及将数据插入到数据库中。此存储过程将由调度程序部署 - 最有可能是 azure webJobs,因为据我所知 Azure SQL 不支持 db 作业。

为什么我想到使用 C#

假设我可以从我的服务器加载 DAL 库,并利用 OOP - 我想我可以为这个问题实现一个对编码器更友好的解决方案 - 至于性能,我认为它会更好或至少在比较中不明显。

为什么我想到使用 T-SQL

我实际上并不认为这是一个更好的解决方案,但可能有一些我忽略的东西可能是 T-SQL 的一个很好的例子,因为对我来说它没有任何意义。

最重要的是

有没有办法可以创建一个 T-SQL 存储过程,调用一个带有复杂对象作为参数的 C# 存储过程 - 执行我的算法并返回要插入的数据,然后从 T-SQL 插入它?似乎是最优雅、性能最好的 (?) 解决方案(假设它是可能的,并且从 T-SQL 调用 CLR 没有任何缺点)。

Sol*_*zky 14

首先,要明确:SQLCLR / .NET / C# / VB.NET 无法查询数据库。只有 T-SQL 可以查询 SQL Server。因此,为了让 SQLCLR 代码以任何方式获取数据或与 SQL Server 交互,它必须SqlConnection像任何其他 .NET 应用程序一样建立一个.NET 应用程序,并提交 T-SQL,或执行存储过程。

是的,您可以从 T-SQL 调用 SQLCLR(无论是 C# 还是 VB.NET 都无所谓)存储过程、标量函数或表值函数 (TVF) 等。事实上,这是调用 SQLCLR 对象的唯一方法。在程序集中定义的表示 SQLCLR 对象的类需要定义指向这些类的 T-SQL 包装对象,以便可以调用它们。

可以将复杂对象发送到 SQLCLR 对象中,但遗憾的是不能通过表值参数 (TVP) 来完成。但是,您可以在 T-SQL 中创建一个本地临时表,填充它,并在 SQLCLR 对象中从中选择,或者您可以将复杂数据打包到 XML 中并将其作为参数传递给 SQLCLR 对象,该对象将通过XmlReader.

为了有效地取回数据,您可以:

  • INSERT从 SQLCLR 存储过程发出语句
  • 创建一个接受 TVP 并从 SQLCLR 对象调用它的 T-SQL 存储过程
  • 从在 INSERT 语句中使用的 SQLCLR 对象返回结果集,如INSERT INTO ... EXECSQLCLR 存储过程或INSERT INTO ... SELECTSQLCLR TVF。

就加载您的 DAL 库而言,这可以工作,但也可能会出现复杂情况。这完全取决于您的库正在做什么以及正在引用哪些框架库。SQL Server 的 CLR 主机仅支持一小部分 .NET Framework 库,因此如果您的库引用了其他未被“批准”的库,则您将需要手动加载这些库及其任何依赖库(如果有的话)。这里的复杂因素是加载不受支持的 .NET Framework 库需要将它们设置为UNSAFE,这反过来又需要将数据库设置为TRUSTWORTHY ON. 另一个复杂问题是,加载 .NET Framework 库一开始甚至可能不可能,因为 SQL Server 内部的 CLR 主机只允许纯 MSIL 程序集,因此您无法加载混合模式程序集。某些 .NET Framework 库当前是混合模式并且不会加载。其他人目前是“纯”的并且会加载,但不能保证在未来的 .NET Framework 更新中这些不会被更改为混合模式程序集。如果/当发生这种情况(并且已经发生,例如System.ServiceModel),那么您将需要重新编码您的程序集。因此,最好坚持使用受支持的框架库,因为这些库可以保证在未来的升级中继续工作。

请记住,应用程序域是每个数据库、每个所有者(程序集的)。这意味着,对于任何特定的 SQLCLR 对象,所有会话/SPID 都将调用相同的代码并共享相同的内存。这就是为什么需要将方法声明为static. 这就是为什么尝试使用非readonly静态类变量需要将程序集标记为UNSAFE:因为此类变量的值可以被另一个会话/SPID 覆盖(例如:SQLCLR 程序集在多个查询同时运行时抛出错误) .

关于问题中的这一陈述:

至于性能,我认为相比之下它会更好或至少不明显。

我不会在这里假设任何事情。有很多因素会影响任一方向的性能:

  • 某些数据类型在 SQL Server 内存和 .NET AppDomain 内存之间来回传输的速度比其他数据类型快
  • 你如何在 C# 中编码你的算法(我最近为已经得到另一个使用 LINQ 的建议的人写了一个简单的函数——LINQ 版本用了 375 - 400 毫秒来做同样的事情,我的版本用了更多的行,只用了2 - 9 毫秒完成,41 倍 - 200 倍的差异!)
  • 您拥有多少数据(有时 T-SQL 使用较小的值会更快——通常是字符串——其中 SQLCLR 使用更长的值可能会更快)
  • 您是否遵循最佳实践?如果您正在编写一个确定性 SQLCLR 标量函数并且没有将其标记为IsDeterministic=true,那么您将无法获得它能够参与并行计划的好处(这是 T-SQL UDF 无法做到的)。
  • 等等

因此:不要假设;测试!

如果您想了解有关使用 SQLCLR 的更多信息,您可能想查看我正在 SQL Server Central 上撰写的关于此主题的系列文章:SQLCLR 的阶梯(需要免费注册才能访问其内容)。有几篇文章和更多文章,以及大量示例来说明各种功能、安全性等。


关于 Azure SQL 数据库:请注意,虽然SAFE在 2014 年底添加了对 SQLCLR(仅程序集)的支持,但它最近(据说是暂时)被删除了。客户于 2016 年 4 月 8 日收到电子邮件,称该功能将于 2016 年 4 月 15 日删除。我能找到的最正式的通知是reddit 上的这个帖子

顾客:

Azure SQL 中对 CLR 程序集的支持是否会很快结束?
我问这个是因为我今天早上(据说)收到一封来自 Microsoft 的电子邮件,指出 4 月 15 日(!!!)之后将不再支持此功能。

Jan,Azure SQL DB 团队中负责性能和弹性池的 PM:

您收到的电子邮件确实是真实的。正如邮件中所述,我们确定 SQL 数据库中用户定义的 SQL CLR 存在安全风险。迄今为止,还没有已知的漏洞利用。为了帮助保护客户的安全,我们已主动暂停在 Azure SQL 数据库中使用用户定义的 SQL CLR。这意味着用户定义的 SQL CLR 将不再起作用。我们将在六个月内向您更新恢复 SQL CLR 的时间表。

和 Tommy,Azure SQL DB 安全团队的 PM:

此问题仅适用于 Azure SQL 数据库服务,不适用于本地 SQL Server 或 IaaS。

  • Srutzky 所说的,加上:CLR 线程是 SQL Server 中的噩梦。就个人而言,我会使用应用程序服务器并将 CLR 排除在数据库之外。与 SQL Server 内部的 CLR 相比,应用服务器将更容易扩展且性能更高。你仍然可以坚持使用它,我只是在数据库之外使用它。 (6认同)