【问题标题】:When should I use stored procedures?什么时候应该使用存储过程?
【发布时间】:2026-02-03 00:40:01
【问题描述】:

什么时候应该使用存储过程而不是直接在我的应用程序中编写逻辑?我想获得存储过程的好处,但我也不想让我的应用程序逻辑分布在数据库和应用程序上。

在这方面您能想到什么经验法则吗?

【问题讨论】:

标签: sql database stored-procedures


【解决方案1】:

在 2021 年使用程序是否仍然有意义?

也许在低级别和罕见的情况下,或者如果我们为具有毫无根据的限制的传统公司编写代码,则应该选择存储过程。

  • 如果整个逻辑都在数据库中,我是否需要一个 dba 来更改它?
    • 没有。在现代平台中,不允许 DBA 更改业务逻辑。
  • 在没有开发或暂存阶段的情况下对存储过程进行热修改,这是一个疯狂的想法。
  • 与使用初级开发人员能够维护的任何现代语言的 OOP 对象相比,维护具有数十行、游标和其他低级数据库功能的过程有多容易?
    • 这不需要更多的 cmets。
  • 出于安全原因向我的开发团队隐藏表格对我来说听起来很疯狂,在这个时代,敏捷性和良好的文档就是一切。
    • 拥有现代数据库的现代开发团队,不必担心安全问题。此外,他们需要访问沙盒版本的数据库,以减少交付时间。
  • 随着现代 ORM、ESB、ETL 和 CPU 功率的不断增加,存储过程不再是一种选择。我应该在这些工具上投入时间和金钱,最终创建一个大存储过程吗?
    • 当然不是。

【讨论】:

    【解决方案2】:

    根据我的经验,存储过程对于构建报告数据库/管道非常有用,但是,我认为您应该避免在应用程序中使用存储过程,因为它们会阻碍团队的速度以及在其中构建查询的任何安全风险使用现代工具/框架可以减轻应用程序的负担。

    我们为什么要避免它?

    • 为了避免应用程序和数据库之间的紧密耦合。如果我们使用存储过程,将来我们将无法轻松更改底层数据库,因为我们必须:

      1. 将存储过程从一个数据库(例如 DB2)迁移到另一个数据库(例如 SQL Server),这可能非常耗时或...
      2. 将所有查询迁移到应用程序本身(或可能在共享库中)
    • 因为代码优先是一回事。有几个 ORM 可以让我们以任何数据库为目标,甚至可以管理表模式,而无需接触数据库。实体框架或 Dapper 等 ORM 允许开发人员专注于构建功能,而不是编写存储过程并将它们连接到应用程序中。

    • 这是开发人员为了提高工作效率而需要学习的另一件事。相反,他们可以将查询编写为应用程序的一部分,从而使构建新功能和/或修复错误的开发人员更容易理解、维护和修改查询。

    最终,这取决于开发人员最喜欢什么。 如果开发人员有丰富的 SQL 背景,他们可能会使用存储过程。 如果开发人员有很多应用程序开发经验,他们可能更喜欢代码查询。就个人而言,我认为在代码中进行查询可以使开发人员更快地移动,并且可以通过确保团队遵循最佳实践(例如参数化查询、ORM)来缓解安全问题。存储过程不是系统安全的“灵丹妙药”。

    【讨论】:

      【解决方案3】:

      我倾向于避免使用存储过程。调试工具往往更原始。错误报告可能更难(与您的服务器的日志文件相比),至少对我而言,它似乎只是添加了另一种语言而没有真正的收获。

      在某些情况下它很有用,尤其是在服务器上处理大量数据时,当然还有在代码中无法执行的数据库触发器。

      除此之外,我倾向于在代码中完成所有操作,并将数据库视为大量数据转储,而不是我在其上运行代码的东西。

      考虑Who Needs Stored Procedures, Anyways?

      适用于现代数据库和现实世界 使用场景,我相信一个Stored 程序架构有严重的 缺点和实用性不大 益处。 存储过程应该是 考虑的数据库汇编语言: 仅用于最高性能 危急情况。

      Why I do not use Stored Procedures:

      你能做的绝对最糟糕的事情, 这在 微软开发世界,是为了 split related functionality between sproc's and middle tier code。 呸呸呸。你只需编写代码 易碎,你会增加 理解的智力开销 一个系统。

      【讨论】:

      • 错误的例子并不能消除正确使用方法时的优势。
      • @cletus 这没有意义。一个例子总是有限的,永远不能证明一个概括。
      • "数据库汇编语言"??这是荒谬的。它是same_language——transact-sql。我不主张在中间层和数据层之间拆分业务逻辑,但您的中间层应该专门使用 SP 来处理数据。这是性能问题,除非您使用一些不预编译存储过程的糟糕 rdbms。我想听一首“Serious Downside”。
      • 不能再不同意了..“没有真正的收获”?避免往返不是收获吗? (在大多数情况下,SP 是合适的,它们可以节省 MULTIPLE 往返行程)。组织数据库访问代码并通过这样做来提高可维护性并不是一种收获?
      • 就像以前汇编语言的性能提升很重要一样,现在它对于大多数用途来说已经无关紧要了。曾经,非托管代码(例如 C/C++)的性能优势是一个重要因素。现在它(大部分)不是。当然还有其他理由使用上述内容。 SP 的任何假设性能提升在今天同样(大部分)无关紧要(除了极端情况)。可维护性、易于开发(例如单元测试)等更为重要
      【解决方案4】:

      我同意应该经常使用它们并且很好地使用它们。

      我认为非常有说服力和非常有用的用例是,如果您要接收大量原始信息,这些信息应该被分成几个表,其中一些数据可能已经存在并且需要记录通过外键 id 连接,那么你可以只 IF EXISTS 检查,如果没有则插入,如果有则返回键,从长远来看,这使得一切更加统一、简洁和可维护。

      我建议反对使用它们的唯一情况是,如果您在查询之间进行大量逻辑或数字运算,最好在应用服务器中完成,或者如果您正在为一家公司工作在代码中保留所有的逻辑对于可维护性/理解正在发生的事情很重要。如果您有一个 git 存储库,其中包含任何人需要的所有内容并且易于理解,那将非常有价值。

      【讨论】:

        【解决方案5】:

        当所有代码都在存储过程中时,在需要时重构数据库要容易得多。对逻辑的更改也更容易推动。性能调优也容易得多,而且对于大多数数据库应用程序而言,迟早需要进行性能调优。

        【讨论】:

        • 对谁来说更容易?必须构建应用程序、修改表以支持新功能并编写查询以获取/更新应用程序中所需数据的应用程序开发人员?我觉得这很难相信。
        【解决方案6】:

        这也取决于您的受众。易于安装和跨 DBMS 的可移植性对您来说重要吗?

        如果您的程序应该易于安装并且易于在不同的数据库系统上运行,那么您应该远离存储过程,并注意代码中的不可移植 SQL。

        【讨论】:

        • 是的,人们总是将此作为您应该避免使用非标准 SQL 的原因,但实际上几乎没有理由更换数据库供应商(我想我从未见过除了最琐碎的项目之外,它在 IT 领域用了 30 年的时间完成)
        【解决方案7】:

        如果您谈论的是业务逻辑,而不仅仅是“我应该使用一般存储过程”,我会说您应该在执行基于大型集合的操作时将业务逻辑放入存储过程中,或者执行逻辑需要大量时间的任何其他时间从应用程序调用数据库的次数。

        【讨论】:

          【解决方案8】:

          除了速度和安全性考虑之外,我倾向于尽可能多地使用存储过程,以便于维护和更改。如果你把逻辑放在你的应用程序中,后来发现sql逻辑有错误或者需要以某种方式不同地工作,在很多情况下你必须重新编译和重新部署整个应用程序(特别是如果它是一个客户端应用程序,如WPF , Win-Forms 等)。如果您将逻辑保存在存储过程中,您所要做的就是更新过程,而您永远不必接触应用程序。

          【讨论】:

            【解决方案9】:

            您可能会受益的特定场景涉及围绕“(n+1)”可伸缩性问题的情况。任何类型的多维/分层情况都可能涉及这种情况。

            另一种场景将涉及在处理表时执行某些协议的用例(提示:定义可能涉及哪些事务的步骤),这可能受益于引用的局部性:在服务器中,查询可能会受益。 OTOH,您可以直接向服务器提供一批语句。特别是当您在 XA 环境中并且必须访问联合数据库时。

            【讨论】:

              【解决方案10】:

              哇...我要直接逆流而上,说“几乎总是”。原因有很多——我相信其他人会争论其中的一些/很多。但是我已经开发了应用程序,无论是否使用存储过程作为数据访问层,我的经验是编写良好的存储过程使编写应用程序变得更加容易。然后是有据可查的性能和安全优势。

              【讨论】:

              • 充分记录了性能和安全优势。只是想重申一下。我们永远不会将 TSQL 放在应用程序中。 SQL进入一个存储过程,该过程是从代码中调用的。没有任何代码触及到一个 select 语句。是否运行 SP 是预编译代码和解释代码之间的区别——您更喜欢哪一个?您的问题的答案是“总是”。
              • “有据可查”的性能项目实际上不是问题,具体取决于您使用的数据库引擎。但是,出于安全考虑,您应该始终使用存储过程。使用 procs 时,您可以拒绝直接表访问,从而完全保护自己免受大多数形式的破坏性 sql 注入。否则,您将依赖代码来阻止它;并非所有程序员都是平等的。
              • @Chris Lively:参数化查询对于 sql 注入是最安全的。见palisade.plynt.com/issues/2006Jun/injection-stored-procedures
              • @KB:首先,sql 注入只是针对数据库的一种攻击媒介。其次,参数化查询不会让您免于有人上传带有嵌入代码的恶意文档。如果开发人员忘记这样做,它也不会拯救你。然而,一开始就不允许直接访问表将阻止所有这些。
              • 我不能说我曾经不同意一个公认的答案,就像我不同意这个答案一样。当方案不断变化时,教条的“一切都是 SP”会引起如此多的流失。您不会阻止必须更改代码。您仍然必须从代码中执行 SQL。而且您使代码无法选择所需的数据。我无法在 SP 上找到所有东西的价值。
              【解决方案11】:

              我在评论中说过,但我要在这里再说一遍。

              安全、安全、安全

              当 sql 代码嵌入到您的应用程序中时,您必须公开底层表以直接访问。这可能起初听起来不错。直到你被一些 sql 注入打乱了数据库中的所有 varchar 字段。

              有些人可能会说他们通过使用魔术引号或其他一些正确转义嵌入的 sql 的方式来解决这个问题。但是,问题是开发人员没有正确转义的一个查询。或者,忘记不允许上传代码的开发人员。或者,被破解的 Web 服务器允许攻击者上传代码。或者,……你明白了。很难涵盖所有的基础。

              我的意思是,所有现代数据库都内置了安全性。您可以简单地拒绝直接表访问(选择、插入、更新和删除)并强制所有内容通过您的 s'procs。这样一来,通用攻击将不再有效。相反,攻击者将不得不花时间了解您系统的私密细节。这增加了他们在时间上花费的“成本”,并阻止了偷渡和蠕虫攻击。

              我知道我们无法确保自己免受所有问题的影响,但如果您花时间构建应用程序,以便破解它的成本远远超过收益,那么您将大大降低数据丢失的可能性。这意味着利用所有可用的安全工具。

              最后,关于不使用 s'procs 的想法,因为您可能必须移植到不同的 rdbms:首先,大多数应用程序不会更改数据库服务器。其次,如果真的有可能,您无论如何都必须使用 ANSI sql 进行编码;你可以在你的过程中做。第三,无论如何,您都必须重新评估您的所有 sql 代码,并且当该代码在一个地方时,这会容易得多。第四,所有现代数据库现在都支持 s'procs。第五,当使用 s'proc's 时,您可以针对运行它的数据库自定义调整 sql,以利用该特定数据库的 sql 扩展。

              【讨论】:

              • 还有一个好处,在一个已经发布的应用程序中更改 s'procs 中的 sql 代码比由于一个较小的查询更改而重新部署整个应用程序更容易。
              • 完全同意。仅在低级别和罕见的情况下,应使用过程。
              【解决方案12】:

              我倾向于总是使用存储过程。就个人而言,我发现它使一切都更容易维护。然后是安全和性能方面的考虑。

              只要确保您编写干净、布局合理且文档齐全的存储过程即可。

              【讨论】:

                【解决方案13】:

                我在这方面有过一些非常糟糕的经历。

                我不反对存储过程代替它们,但是无偿使用存储过程可能会非常昂贵。

                首先,存储过程在数据库服务器上运行。这意味着,如果您有一个包含 50 台网络服务器和一台数据库服务器的多服务器环境,而不是将工作负载分散到 50 台廉价机器上,而是加载一台昂贵的机器(因为数据库服务器通常构建为重量级服务器)。而且您正在冒着造成单点故障的风险。

                其次,仅在存储过程中编写应用程序并不容易,尽管我遇到了一个付出了超人努力的尝试。所以你最终会得到一些维护成本很高的东西:它是用 2 种不同的编程语言实现的,而且源代码通常也不在一个地方,因为存储过程最终存储在 DBMS 中,而不是源存档中。假设有人曾经管理/打扰过 o 将它们从数据库服务器中拉出并完全对它们进行源存档。

                因此,除了相当混乱的应用架构之外,您还限制了能够维护它的合格黑猩猩的数量,因为需要多种技能。

                另一方面,存储过程非常有用,如果:

                1. 您需要跨多个系统维护某种数据完整性。也就是说,存储的逻辑不属于任何单个应用程序,但您需要所有参与应用程序的行为一致。在现代应用程序中以外键和触发器的形式出现一定程度的这种情况几乎是不可避免的,但偶尔也可能需要进行重大编辑和验证。

                2. 您需要的性能只能通过在数据库服务器本身上运行逻辑而不是作为客户端来实现。但是,正如我所说,当您这样做时,您正在消耗 DBMS 服务器的全部系统资源。因此,您有必要确保如果有大量违规操作可以卸载到客户端,您可以将它们分离出来并将最关键的内容留给 DBMS 服务器。

                【讨论】:

                • 如果你有“50 个网络服务器”,我希望你至少有一个 DBA。谁碰巧知道TSQL。存储过程是用什么写的。没有什么乱七八糟的东西——乱七八糟的不是使用存储过程。
                • 存储过程与您需要多少数据库服务器无关。原因很简单,不管是proc还是嵌入式SQL,DB服务器仍然要运行代码。
                • “因为存储过程最终存储在 DBMS 中,而不是源存档中” 糟糕的开发人员。我们总是,总是将存储过程保存在源存档中。而且它们不必从数据库中取出并放入源存档,因为它们从源存档流入所有数据库。 (除非它们正在开发中开发。)
                • >>我们始终将存储过程保存在源存档中。绝对地!没有任何东西进入我们部署的不在源代码控制中的应用程序。什么都没有。
                【解决方案14】:

                这完全取决于您的环境。这个问题的答案真的不是编码问题,甚至不是分析问题,而是业务决策。

                如果您的数据库只支持一个应用程序,并且与它合理地紧密集成,那么出于灵活性的原因,最好将您的逻辑放在您的应用程序中。在这些情况下,将数据库简单地作为使用通用功能的普通数据存储库来处理几乎不会让您失去灵活性并获得灵活性 - 与供应商、实施、部署和许多其他方面 - 并且许多“数据库用于数据”的纯粹论点都得到了证明真的。

                另一方面,如果您正在处理公司数据库,通常可以通过多个访问路径来识别该数据库,那么强烈建议您尽可能降低安全性。至少应该启用所有适当的约束,并且如果可能的话,应该仅通过视图和过程访问数据。在这些情况下,抱怨的程序员应该被忽略,因为......

                1. 对于企业数据库,资产是有价值的,无效的数据或操作可能会产生危及业务的后果。您最关心的是保护业务,而不是您的编码人员访问是否方便。
                2. 根据定义,此类数据库由多个应用程序访问。您需要使用存储过程提供的抽象,以便在升级应用程序 A 并且您没有资源升级应用程序 B 时更改数据库。
                3. 类似地,将业务逻辑封装在 SP 中而不是应用程序代码中,与将此类逻辑嵌入到应用程序代码中相比,可以更轻松、更可靠地在整个业务中实现对此类逻辑的更改。例如,如果税收计算发生变化,如果必须在一个 SP 中更改计算,而不是在多个应用程序中更改,那么它的工作量会减少,而且更健壮。这里的经验法则是业务规则应该在最接近其唯一数据的点实现 - 因此,如果您有一个专业应用程序,那么该应用程序的逻辑可以在该应用程序中实现,但逻辑更广泛适用应在 SP 中实施。

                因使用或不使用 SP 而陷入宗教战争的编码人员通常只在一种或另一种环境中工作,因此他们将有限的经验推断为铸铁的立场 - 这在上下文中确实是完全可以辩护和正确的他们来自那里,但错过了大局。与往常一样,您应该根据业务/客户/用户的需求而不是您喜欢哪种类型的编码方法来决定。

                【讨论】:

                  【解决方案15】:

                  我在 3 个场景中的 1 个场景中使用了存储过程:

                  速度 当速度至关重要时,存储过程提供了一种极好的方法

                  复杂性 当我更新几个表并且代码逻辑可能会改变时,我可以更新存储的过程并避免重新编译。存储过程是一种出色的黑盒方法,可以一次更新大量数据。

                  交易 当我处理跨越多个表的插入、删除或更新时。我将整个事情包装在交易中。如果出现错误,很容易回滚事务并抛出错误,避免数据损坏。

                  底部的 2 个代码非常实用。但是,当复杂的事务级操作很重要时,存储过程提供了一种黑盒工作方法。否则,坚持代码级数据库操作。

                  安全曾经是原因之一。然而,有了 LINQ 和其他 ORM,代码级 DAL 操作比过去更加安全。存储过程是安全的,但像 LINQ 这样的 ORM 也是安全的。

                  【讨论】:

                    【解决方案16】:

                    存储过程是一种收集操作的方法应该在数据库端一起完成,同时仍然将它们保存在数据库端

                    这包括:

                    • 从一个行源填充多个表
                    • 根据不同的业务规则检查多个表
                    • 使用基于集合的方法无法有效执行的操作

                    等等

                    存储过程的主要问题是它们难以维护。

                    因此,您应该使存储过程像所有其他代码一样易于维护

                    我的博客中有一篇关于此的文章:

                    【讨论】:

                      【解决方案17】:

                      基本上当您必须执行涉及不需要从数据库中取出的数据的操作时。例如,您想用另一个表中的数据更新一个表,如果您可以一次性完成所有操作,那么将数据取出然后再输入数据库是没有意义的。

                      另一种可以接受使用存储过程的情况是,当您 100% 确定永远不会将应用程序部署到其他数据库供应商时。如果您是 Oracle 商店,并且您有许多应用程序与同一个数据库通信,那么使用存储过程以确保所有应用程序以一致的方式与数据库通信可能是有意义的。

                      【讨论】:

                        【解决方案18】:

                        我们使用存储过程来满足我们所有的报告需求。他们通常可以更快地检索数据,并且以报告可以直接吐出的方式检索数据,而无需进行任何类型的计算或类似操作。

                        我们还将使用存储过程来处理我们需要执行的复杂或复杂的查询,如果它们在我们的代码库中,将难以阅读。

                        【讨论】:

                          【解决方案19】:

                          它在封装和 DRY 哲学中也非常有用。例如,我使用存储函数在一个表中进行计算,我需要在代码中进行多个查询。这样我可以使用更好的性能以及确保始终以相同的方式完成计算。

                          我不会将它用于更高的功能或逻辑,应该在架构的业务逻辑层中,而是专注于模型层,其中功能显然专注于数据库设计和更改数据库设计的可能灵活性无需将 API 中断到其他层。

                          【讨论】:

                            【解决方案20】:

                            对我来说,复杂的数据库查询往往以存储过程结束。另一个需要考虑的想法是,您的数据库可能与应用程序完全分离和不同。假设您运行一个 Oracle DB,并且您实际上是在为您组织中的其他应用程序开发人员构建一个 API 来调用。你可以对他们隐藏复杂的东西,并在其位置提供一个存储过程。

                            一个非常简单的例子:

                            registerUser(username, password)
                            

                            可能最终会运行一些不同的查询(检查它是否存在,在首选项表中创建条目等),并且您可能想要封装它们。

                            当然,不同的人会有不同的观点(DBA 与程序员)。

                            【讨论】:

                            • +1 非常好的一点,即使在编写有限的桌面应用程序时我也会这样做,因为将复杂的数据库维护处理打包并隐藏在应用程序代码之外是件好事。
                            最近更新 更多