【问题标题】:Are CLR stored procedures preferred over TSQL stored procedures in SQL 2005+?在 SQL 2005+ 中,CLR 存储过程是否优于 TSQL 存储过程?
【发布时间】:2010-09-08 15:52:57
【问题描述】:

我目前的看法是不,更喜欢 Transact SQL 存储过程,因为它们重量更轻且(可能)性能更高的选项,而 CLR 过程允许开发人员进行各种恶作剧。

但是最近我需要调试一些写得很糟糕的 TSQL 存储过程。像往常一样,我发现许多问题是由于最初的开发人员没有真正的 TSQL 经验,他们专注于 ASP.NET / C#。

因此,使用 CLR 过程首先会为这类开发人员提供更熟悉的工具集,其次,调试和测试工具更强大(即 Visual Studio 而不是 SQL Management Studio)。

我很想听听您的经历,因为这似乎不是一个简单的选择。

【问题讨论】:

    标签: .net sql-server tsql sqlclr


    【解决方案1】:

    编写良好、经过深思熟虑的 T-SQL 和 CLR 都有一些地方。如果某些函数不经常调用并且如果它需要 SQL Server 2000 中的扩展过程,CLR 可能是一个选项。在数据旁边运行诸如计算之类的东西也可能很有吸引力。但是通过引入新技术来解决糟糕的程序员听起来是个坏主意。

    【讨论】:

    • “但是通过引入新技术来解决糟糕的程序员听起来是个坏主意” - 很好,谢谢。当我看到开发人员如此专注于 ASP.NET 却几乎不惜一切代价时,我感到很担心。
    【解决方案2】:

    CLR 存储过程并不意味着取代基于集合的查询。如果您需要查询数据库,您仍然需要将 SQL 放入您的 CLR 代码中,就像它嵌入在常规代码中一样。这将是一种浪费。

    CLR 存储过程主要用于两件事情:1) 与操作系统的交互,例如从文件读取或在 MSMQ 中删除消息,以及 2) 执行复杂的计算,尤其是当您已经以 . NET 语言进行计算。

    【讨论】:

    • 我会补充说,当您需要正则表达式时,它们特别好。
    【解决方案3】:

    在 SQL Server 中托管 CLR 旨在为数据库开发人员提供更灵活的选择,以便他们设法完成任务。正如其他人所提到的,SQL 非常适合对数据集的操作和修改。任何使用复杂的业务/域规则进行过大型应用程序开发的人都可能会告诉您 - 尝试使用纯 SQL(有时在单个宏查询中)强制执行其中一些规则可能会变得非常糟糕。

    只有某些任务可以以程序或 OO 方式更好地处理。通过选择使用 .NET 代码来分解逻辑序列,查询操作可以变得更易于阅读和调试。使用过 CLR 存储过程后,我可以告诉您单步执行调试器确实可以更轻松地跟踪数据库级别发生的事情。

    仅举一个例子,我们在这里经常使用 CLR 存储过程作为动态搜索查询的“网关”。假设一个搜索请求最多可以有 30 个不同的搜索参数。用户显然不会使用全部 30 个,所以传入的数据结构会有 30 个参数,但大多是 DBNULL。出于明显的安全原因,客户端没有选项来生成动态语句。生成的动态语句在内部生成,无需担心外部“额外”。

    【讨论】:

      【解决方案4】:

      一般来说,如果您有一些不需要与数据库进行太多交互的东西,则可以使用 CLR。因此,假设您正在解析或解码一个值。这在 CLR 中更容易做到,然后返回值。

      尝试在 CLR 中执行复杂查询是不可行的。

      顺便说一句,这在 2008 年也没有改变。

      【讨论】:

        【解决方案5】:

        我认为这两者并不等同...适合相互对抗。
        CLR 集成应该逐步淘汰过去的“扩展存储过程”。我们有一些其中一些在我们的工作场所......本质上是对 SQL 数据的处理/逻辑块,这些块太难/不可能通过传统的 DB 存储过程/T SQL 来完成。因此,他们将其编写为 C++ DLL 中的扩展存储过程,可以类似地调用。 Now they have been phased out and CLR integration is the replacement

        • DB 存储过程:如果可以在 T SQL 存储过程中完成,那就去做吧。
        • CLR 存储过程:如果逻辑过于复杂或乏味而无法通过 T SQL 执行... ) 使用这种方法。

        【讨论】:

        【解决方案6】:

        除了文件系统访问(CLR procs 具有非常明显的优势)之外,我会使用 T-SQL procs。如果您有特别复杂的计算,您可以将该部分放入 CLR function 并从您的 proc 中调用它(udf 是我发现 CLR 集成真正闪耀的地方)。然后,您可以为任务的特定部分获得 CLR 集成的好处,同时尽可能多地将存储的过程逻辑保留在数据库中。

        【讨论】:

          【解决方案7】:

          鉴于您所说的,我宁愿您让开发人员在 t-SQl 和一般数据库方面接受适当的培训,而不是允许他们通过允许他们在 CLR 中执行 t-sql 任务来对性能造成可能更大的损害。不了解数据库的开发人员以此为借口避免以最有利于数据库性能的方式做事,因为他们希望采用他们认为更容易的方法。

          【讨论】:

          • 一个开发者到另一个开发者:“嘿,我有一个很酷的主意!让我们使用我们的对象 GUID 作为主键!”
          • @ValentinoVranken - 或者像微软的一些开发人员一样 - 嘿,让我们从对象中获取 GUID,获取它们的前半部分,然后反转它们并将它们用作键(在注册表中)。是的,无论是谁写了Installers 部分——我们正看着你。
          【解决方案8】:

          它总是归结为适合这项工作的正确工具,因此这实际上取决于您要完成的工作。

          但是,作为一般规则,您是对的,CLR proc 具有更大的开销,并且永远不会在 T-SQL 之类的集合操作上执行。我的指导方针是全部在 T-SQL 中完成,除非你需要的东西在 T-SQL 中变得过于复杂。然后,更加努力地让 T-SQL 方法发挥作用。 :-)

          CLR proc 非常棒,并且确实有它们的位置,但它们的使用应该是例外,而不是规则。

          【讨论】:

            【解决方案9】:

            SQL Server 联机丛书的page on the subject 列出了这些好处:

            • 更好的编程模型。 .NET Framework 语言在许多方面都比 Transact-SQL 更丰富,提供以前 SQL Server 开发人员无法使用的构造和功能。开发人员还可以利用 .NET Framework 库的强大功能,该库提供了广泛的类集,可用于快速有效地解决编程问题。

            • 提高了安全性。 托管代码在由数据库引擎托管的公共语言运行时环境中运行。 SQL Server 利用这一点为 SQL Server 早期版本中提供的扩展存储过程提供了一种更安全、更安全的替代方案。

            • 能够定义数据类型和聚合函数。用户定义类型和用户定义聚合是两个新的托管数据库对象,它们扩展了 SQL Server 的存储和查询能力。

            • 通过标准化环境简化开发。 数据库开发已集成到 Microsoft Visual Studio .NET 开发环境的未来版本中。开发人员使用与编写中间层或客户端层 .NET Framework 组件和服务相同的工具来开发和调试数据库对象和脚本。

            • 提高性能和可扩展性的潜力。在许多情况下,.NET Framework 语言编译和执行模型的性能优于 Transact-SQL。

            【讨论】:

            • 不要相信炒作。在某些情况下,CLR 函数/sproc 是更好的选择,但“更好的编程模型”不是其中之一,也没有提高安全性(您确实知道它们在哪个执行上下文下运行,对)并且性能只会更好在某些情况下 - 只有当代码是使用命令式逻辑迭代行集是唯一选择的情况下。 SQL Server 和 .NET 运行时之间的上下文切换也会给您带来很大的性能损失。
            • @gbjbaanb:基于 CLR 的代码的安全模型肯定比 T-SQL 之外的其他扩展功能的选项更安全,即:扩展存储过程、OLE 自动化过程(即 sp_OA*)、和 SQLCMD。 CLR 模型提供更精细的控制以及使用模拟的能力。我写了两篇文章(SQLCLR 较长系列的一部分),详细介绍了安全主题(需要免费注册):sqlservercentral.com/articles/SQLCLR/109905sqlservercentral.com/articles/SQLCLR/112888
            【解决方案10】:

            我们遇到了一个 CLR 函数的情况,该函数在常规 SQL 过程中被调用了数千次。这是从另一个系统导入数据的过程。该函数验证了数据并很好地处理了空值。

            如果我们在 TSQL 中进行操作,proc 大约在 15 秒内完成。如果我们使用 CLR 函数,proc 会在 20 - 40 分钟内完成。 CLR 函数看起来更优雅,但据我们所知,每次使用 CLR 函数都会触发一次启动。因此,如果您使用一个 CLR 函数完成了一项大型操作,那很好,因为与操作时间相比,启动时间很短。或者,如果您调用 CLR 函数的次数不多,则该函数的所有调用的总启动时间会很短。但要小心循环。

            此外,为了可维护性,最好不要使用超过您真正需要的语言。

            【讨论】:

            • 假设您的 CLR 函数是确定性的,它是否在 SqlFunction 属性中标记为 IsDeterministic=true?如果不是,这可能是 20 到 40 分钟执行时间的一个重要因素。另外,您使用的是哪个版本的 SQL Server? SQL Server 2012 中针对确定性标量 CLR 函数进行了性能改进。此外,传入或传出 NVARCHAR(MAX) 而不是 NVARCHAR(4000) 也会影响性能。
            【解决方案11】:

            我会添加几个可能没有提到的使用 CLR 的理由。

            • 替换和扩展基本查询、非查询和标量 sql 函数。
              A) 可以根据定义的要求集成错误报告和警报。 B) 轻松定义调试级别。 C) 以更简单的方式与外部 SQL 服务器交互
            • 将旧代码移至托管环境。

            【讨论】:

              【解决方案12】:

              我针对类似问题发布了以下答案:Advantage of SQL SERVER CLR。 不过,我将在这里补充一点,C# / VB.net / etc 是一种比 T-SQL 更适合的语言,应该成为使用 SQLCLR 而不是 T-SQL 的原因。如果有人不知道如何在 T-SQL 中完成某事,请首先寻求帮助以找到 T-SQL 解决方案。如果一个不存在,然后走CLR路线。


              SQL Server 中的 SQLCLR / CLR 集成只是帮助解决某些(不是全部)问题的另一种工具。有些事情它比纯 T-SQL 做得更好,有些事情只能通过 SQLCLR 完成。我为 SQL Server Central 写了一篇文章,Stairway to SQLCLR Level 1: What is SQLCLR?(阅读那里的文章需要免费注册),解决了这个问题。基础是(有关详细信息,请参阅链接文章):

              • 流式表值函数 (sTVF)
              • 动态 SQL(在函数内)
              • 更好地访问外部资源/替换 xp_cmdshell
                • 传递数据更容易
                • 取回结果集的多列更容易
                • 没有外部依赖项(例如 7zip.exe)
                • 通过模拟提高安全性
              • 多线程能力
              • 错误处理(函数内)
              • 自定义聚合
              • 自定义类型
              • 修改状态(在函数内且不带OPENQUERY / OPENROWSET
              • 执行存储过程(只读;在函数内且不带OPENQUERY / OPENROWSET
              • 性能(注意:不是在所有情况下都意味着,但在某些情况下肯定取决于操作的类型和复杂性)
              • 可以捕获输出(即发送到 SSMS 中“消息”选项卡的内容)(例如,PRINTRAISERROR严重性 = 0 到 10)——我忘了​​提到这个在文章中;-)。

              要考虑的另一件事是,有时能够在应用程序和数据库之间共享代码是有益的,这样数据库就可以深入了解某些业务逻辑,而不必构建自定义的、仅限内部的屏幕来访问该逻辑应用程序代码。例如,我曾在一个系统上工作,该系统从客户那里导入数据文件,并使用大多数字段的自定义散列并将该值保存到数据库中的行中。这允许在再次导入数据时轻松跳过行,因为应用程序将从输入文件中散列值并与存储在行中的散列值进行比较。如果它们相同,那么我们立即知道所有字段都没有改变,所以我们进入下一行,这是一个简单的 INT 比较。但是用于进行哈希的算法仅在应用程序代码中,因此无论是用于调试客户案例还是通过标记至少有一个字段发生更改的行(来自我们的应用程序的更改)来寻找将某些处理卸载到后端服务的方法与在较新的导入文件中查找更改相反),我无能为力。这将是一个在数据库中拥有相当简单的业务逻辑的好机会,即使不是用于正常处理;在数据库中拥有相当于编码值的内容而无法理解其含义,这使得解决问题变得非常困难。

              如果有兴趣在不编写任何代码的情况下查看其中一些功能,SQL#(我是其中的作者)的免费版本具有 RegEx 函数、自定义聚合 (UDA)、自定义类型 (UDT)等。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2017-08-22
                • 1970-01-01
                • 2023-03-20
                相关资源
                最近更新 更多