将 SQL 保留在 C# 源代码或存储过程中的优点/缺点是什么?我一直在与朋友讨论我们正在开发的一个开源项目(C# ASP.NET 论坛)。目前,大多数数据库访问是通过在 C# 中构建 SQL 内联并调用 SQL Server DB 来完成的。因此,我正在尝试确定对于这个特定项目而言,哪一个是最好的。

到目前为止我有:

在代码中的优点:

  • 更易于维护 - 不需要运行 SQL 脚本来更新查询
  • 更容易移植到另一个数据库 - 无需移植

存储过程的优点:

  • 表现
  • 安全
有帮助吗?

解决方案

我不喜欢存储过程

存储过程更易于维护,因为:* 每当您想要更改某些 SQL 时,无需重新编译 C# 应用程序

无论如何,当数据类型发生变化时,或者您想要返回额外的列,或者其他什么情况时,您最终都会重新编译它。总体而言,您可以从应用程序底层“透明地”更改 SQL 的次数非常少

  • 您最终会重用 SQL 代码。

编程语言(包括 C#)有一个令人惊奇的东西,称为函数。这意味着您可以从多个位置调用同一代码块!惊人的!然后,您可以将可重用的 SQL 代码放入其中之一,或者如果您想获得真正的高科技,您可以使用一个可以为您完成此操作的库。我相信它们被称为对象关系映射器,并且现在很常见。

当您尝试构建可维护的应用程序时,代码重复是最糟糕的事情!

同意,这就是为什么存储过程是一件坏事。将代码重构和分解(分解为更小的部分)为函数比将 SQL 分解为函数要容易得多...SQL 块?

你有 4 个网络服务器和一堆使用相同 SQL 代码的 Windows 应用程序现在你意识到 SQl 代码有一个小问题,所以你宁愿......在 1 个位置更改 proc 或将代码推送到所有网络服务器,在所有 Windows 盒子上重新安装所有桌面应用程序(单击一次可能会有所帮助)

为什么您的 Windows 应用程序直接连接到中央数据库?这似乎是一个巨大的安全漏洞,也是一个瓶颈,因为它排除了服务器端缓存。他们不应该通过网络服务或类似于您的网络服务器的方式进行连接吗?

那么,推送 1 个新的存储过程,还是 4 个新的网络服务器?

在这种情况下 推送一个新存储过程更容易,但根据我的经验,95% 的“推送更改”会影响代码而不是数据库。如果您当月向网络服务器推送 20 件事情,向数据库推送 1 件事情,那么如果您向网络服务器推送 21 件事情,向数据库推送 0 件事情,那么您几乎不会损失太多。

更容易进行代码审查。

你能解释一下怎么做吗?我不明白这一点。特别是存储过程可能不在源代码管理中,因此无法通过基于 Web 的 SCM 浏览器等进行访问。

更多缺点:

存储过程存在于数据库中,对外界来说数据库就像一个黑匣子。像想要将它们放入源代码控制之类的简单事情变成了一场噩梦。

还有一个纯粹努力的问题。将一切分解为一个可能是有意义的 百万级 如果你试图向你的 CEO 解释为什么他们只花了 700 万美元来建立一些论坛,但否则为每件小事创建一个存储过程只是额外的苦差事,没有任何好处。

其他提示

目前正在这里的其他几个线程上讨论这个问题。尽管提出了一些支持 Linq to Sql 的很好的论据,但我始终是存储过程的支持者。

在代码中嵌入查询可以将您与数据模型紧密结合。存储过程是契约式编程的一种良好形式,这意味着 DBA 可以自由地更改过程中的数据模型和代码,只要维护存储过程的输入和输出所表示的契约即可。

当查询隐藏在代码中而不是集中在一个易于管理的位置时,调整生产数据库可能会非常困难。

[编辑]这是另一个 目前的讨论

我认为你不能对这个问题投赞成票或反对票。这完全取决于您的应用程序的设计。

我完全反对在三层环境中使用 SP,因为前面有一个应用程序服务器。在这种环境中,您的应用程序服务器用于运行您的业务逻辑。如果您另外使用 SP,您就会开始将业务逻辑的实现分布在整个系统中,并且谁负责什么将变得非常不清楚。最终您将得到一个应用程序服务器,它基本上除了执行以下操作之外什么也不做:

(Pseudocode)

Function createOrder(Order yourOrder) 
Begin
  Call SP_createOrder(yourOrder)
End

所以最后你的中间层运行在这个非常酷的 4 服务器集群上,每个服务器集群配备 16 个 CPU,但它实际上什么也不做!多么浪费啊!

如果您有一个胖 GUI 客户端直接连接到您的数据库或什至更多应用程序,那么情况就不同了。在这种情况下,SP 可以充当某种伪中间层,将应用程序与数据模型分离并提供可控访问。

在代码中的优点:

  • 更易于维护 - 不需要运行 SQL 脚本来更新查询
  • 更容易移植到另一个数据库 - 无需移植

事实上,我认为你已经倒退了。恕我直言,代码中的 SQL 很难维护,因为:

  • 你最终会在相关的代码块中重复自己
  • 许多 IDE 不支持 SQL 作为语言,因此您只有一系列未错误检查的字符串来为您执行任务
  • 数据类型、表名或约束的更改比将整个数据库换成新数据库要普遍得多
  • 随着查询复杂性的增加,难度也会增加
  • 测试内联查询需要构建项目

将存储过程视为从数据库对象调用的方法 - 它们更容易重用,只有一个位置可以编辑,并且如果您更改数据库提供程序,更改将发生在存储过程中而不是代码中。

也就是说,正如 Stu 在我之前所说的那样,存储过程的性能提升是最小的,而且您还不能在存储过程中设置断点。

我发现在存储过程中进行大量处理会使您的数据库服务器在扩展您的行为时成为一个不灵活的点。

然而,在你的程序中而不是在 sql-server 中进行所有的运算, 可能 如果您有多个运行代码的服务器,则允许您进行更大的扩展。当然,这不适用于仅执行正常获取或更新的存储过程,而是适用于执行更多处理(例如循环数据集)的存储过程。

优点

  1. 性能物有所值(避免通过数据库驱动程序/计划重新创建等进行查询解析)
  2. 数据操作没有嵌入到 C/C++/C# 代码中,这意味着我需要查看的低级代码较少。单独列出时,SQL 更简洁且更易于查看。
  3. 由于分离,人们能够更容易地找到和重用 SQL 代码。
  4. 当架构更改时,更改内容更容易 - 您只需为代码提供相同的输出,它就会正常工作
  5. 更容易移植到不同的数据库。
  6. 我可以列出存储过程的个人权限,并控制该级别的访问。
  7. 我可以将数据查询/持久性代码与数据转换代码分开进行分析。
  8. 我可以在存储过程中实现可变条件,并且可以轻松在客户站点进行自定义。
  9. 使用一些自动化工具将我的模式和语句一起转换变得更容易,而不是当它嵌入到我的代码中时我必须寻找它们。
  10. 当您将所有数据访问代码放在一个文件中时,确保数据访问的最佳实践会更容易 - 我可以检查访问非性能表的查询或使用更高级别序列化的查询或在代码中选择 * 等。
  11. 当所有架构更改/数据操作逻辑更改都列在一个文件中时,查找架构更改/数据操作逻辑更改将变得更加容易。
  12. 当 SQL 位于同一位置时,例如搜索和替换编辑变得更加容易。更改/添加所有存储过程的事务隔离语句。
  13. 我和 DBA 人员发现,当 DBA 必须检查我的 SQL 内容时,拥有一个单独的 SQL 文件会更容易/方便。
  14. 最后,您不必担心 SQL 注入攻击,因为您团队中的一些懒惰成员在使用嵌入式 sql 时没有使用参数化查询。

存储过程的性能优势通常可以忽略不计。

存储过程的更多优点:

  • 防止逆向工程(当然,如果使用加密创建)
  • 更好地集中数据库访问
  • 能够透明地更改数据模型(无需部署新客户端);如果多个程序访问相同的数据模型,则特别方便

我落在 代码 边。我们构建了所有应用程序(包括 Web 和客户端)使用的数据访问层,因此从这个角度来看它是 DRY 的。它简化了数据库部署,因为我们只需确保表架构是正确的。它简化了代码维护,因为我们不必查看源代码和数据库。

我对与数据模型的紧密耦合没有太大问题,因为我不知道在哪里可以真正打破这种耦合。应用程序及其数据本质上是耦合的。

存储过程。

如果发生错误或逻辑发生了一些变化,您不必重新编译项目。另外,它允许从不同的来源进行访问,而不仅仅是您在项目中编码查询的地方。

我不认为维护存储过程更困难,您不应该直接在数据库中编码它们,而应该首先在单独的文件中编码,然后您可以在需要设置的任何数据库上运行它们。

存储过程的优点:

更容易进行代码审查。

耦合更少,因此更容易测试。

更容易调整。

从网络流量的角度来看,性能通常更好 - 如果您有游标或类似的游标,则不会多次访问数据库

您可以更轻松地保护对数据的访问,删除对表的直接访问,通过过程加强安全性 - 这还允许您相对快速地找到更新表的任何代码。

如果涉及其他服务(例如报告服务),您可能会发现将所有逻辑存储在存储过程中而不是代码中更容易,并且必须复制它

缺点:

对于开发人员来说更难管理:脚本的版本控制:每个人都有自己的数据库吗,版本控制系统是否与数据库和IDE集成?

在某些情况下,在代码中动态创建的 sql 可以比存储过程具有更好的性能。如果您创建了一个存储过程(比方说 sp_customersearch),由于它必须非常灵活,因此它变得极其复杂,有数十个参数,那么您可能可以在运行时在代码中生成更简单的 sql 语句。

有人可能会说这只是将一些处理从 SQL 转移到 Web 服务器,但总的来说这是一件好事。

这项技术的另一个优点是,如果您在 SQL 探查器中查看,您可以看到生成的查询并对其进行调试,这比查看带有 20 个参数的存储过程调用要容易得多。

我喜欢存储过程,不知道有多少次我能够使用存储过程对应用程序进行更改,而不会给应用程序带来任何停机时间。

Transact SQL 的忠实粉丝,调整大型查询已被证明对我来说非常有用。大约 6 年没有写过任何内联 SQL!

您列出了存储过程的 2 个优点:

性能——并非如此。在 Sql 2000 或更高版本中,查询计划优化非常好,并且已缓存。我确信 Oracle 等公司也在做类似的事情。我认为不再需要存储过程来提高性能。

安全?为什么存储过程会更安全?除非您有一个非常不安全的数据库,否则所有访问都将来自您的 DBA 或通过您的应用程序。始终对所有查询进行参数化 - 永远不要从用户输入中内联某些内容,这样就可以了。

无论如何,这是性能的最佳实践。

Linq 绝对是我现在进行新项目的方式。看到这个 类似的帖子.

@基思

安全?为什么存储过程会更安全?

正如 Komradekatz 所建议的,您可以禁止访问表(对于连接到数据库的用户名/密码组合)并仅允许 SP 访问。这样,如果有人获得您数据库的用户名和密码,他们可以执行 SP,但无法访问表或数据库的任何其他部分。

(当然,执行存储过程可能会为他们提供所需的所有数据,但这取决于可用的存储过程。允许他们访问表格就可以访问所有内容。)

这样想

您有4个Web服务器和一堆Windows应用程序,它们使用相同的SQL代码,现在您意识到SQL代码存在一个小问题,所以您宁愿...........将PROC更改为1位或将代码推向所有Web服务器,重新安装所有桌面应用程序(ClickOnce可能会有所帮助)

我更喜欢存储过程

对PROC进行性能测试也更容易,将其放入查询分析仪集统计信息/时间/时间shet showplan_text上,voila

无需运行探查器即可准确查看正在调用的内容

只是我的 2 美分

我更喜欢将它们保留在代码中(使用 ORM,而不是内联或临时),这样它们就可以被源代码控制覆盖,而不必处理保存 .sql 文件。

此外,存储过程本质上并不是更安全。您可以像内联一样轻松地使用存储过程编写错误的查询。参数化内联查询可以与存储过程一样安全。

尽可能使用您的应用程序代码:处理逻辑。
使用您的数据库发挥其最大作用:存储数据。

您可以调试存储过程,但您会发现调试和维护代码中的逻辑更容易。通常,每次更改数据库模型时,您都会结束重新编译代码。

此外,具有可选搜索参数的存储过程非常低效,因为您必须提前指定所有可能的参数,并且复杂的搜索有时是不可能的,因为您无法预测参数将在搜索中重复多少次。

就安全性而言,存储过程要安全得多。一些人认为无论如何,所有访问都将通过应用程序进行。许多人忘记的是,大多数安全漏洞都来自公司内部。想想有多少开发人员知道您的应用程序的“隐藏”用户名和密码?

此外,正如 MatthieuF 指出的那样,由于应用程序(无论是在桌面还是 Web 服务器上)和数据库服务器之间的往返次数减少,性能可以得到很大的提高。

根据我的经验,通过存储过程对数据模型进行抽象也极大地提高了可维护性。作为过去必须维护许多数据库的人,当面临所需的模型更改时,能够简单地更改一两个存储过程并使更改对所有外部应用程序完全透明,这真是一种解脱。很多时候,您的应用程序并不是唯一指向数据库的应用程序 - 还有其他应用程序、报告解决方案等。因此,通过开放访问表格来追踪所有受影响的点可能会很麻烦。

我还将在加号栏中添加检查,以便将 SQL 编程交给专门从事 SQL 编程的人员,并让 SP 更容易隔离和测试/优化代码。

我看到的一个缺点是许多语言不允许传递表参数,因此传递未知数量的数据值可能会很烦人,并且某些语言仍然无法处理从单个存储过程检索多个结果集(尽管在这方面,后者并不会让 SP 比内联 SQL 更糟糕)。

我参加的有关安全性的 Microsoft TechEd 会议的建议之一是通过存储过程进行所有调用并拒绝直接访问表。这种方法被宣传为提供额外的安全性。我不确定仅仅为了安全性是否值得,但是如果您已经在使用存储过程,那么它不会有什么坏处。

如果将其放入存储过程中,绝对更容易维护。如果涉及到将来可能会发生变化的困难逻辑,那么当您有多个客户端连接时,将其放入数据库中绝对是个好主意。例如,我现在正在开发一个应用程序,它有一个最终用户 Web 界面和一个管理桌面应用程序,这两个应用程序共享一个数据库(显然),并且我试图在数据库上保留尽可能多的逻辑。这是一个完美的例子 干原则.

我坚定地站在存储过程一边,假设你没有作弊并在存储过程中使用动态 SQL。首先,使用存储过程允许 dba 在存储过程级别而不是表级别设置权限。这不仅对于对抗 SQL 注入攻击至关重要,而且对于防止内部人员直接访问数据库和更改内容至关重要。这是帮助防止欺诈的一种方法。除非通过存储程序,否则不得访问包含个人信息(SSN、信用卡号等)或以任何方式创建金融交易的数据库。如果您使用任何其他方法,您的数据库就会对公司中的个人开放,以创建虚假的金融交易或窃取可用于身份盗窃的数据。

存储过程也比从应用程序发送的 SQL 更容易维护和性能调整。它们还使 dba 能够了解数据库结构更改对数据访问方式的影响。我从未遇到过允许动态访问数据库的优秀 dba。

我现在工作的地方使用 Oracle DB 的存储过程。我们还使用 Subversion。所有存储过程都创建为 .pkb 和 .pks 文件并保存在 Subversion 中。我以前做过内联 SQL,这很痛苦!我更喜欢我们在这里做的方式。创建和测试新的存储过程比在代码中进行要容易得多。

有一个

较小的原木

存储过程的另一个小优点尚未提及:当涉及到 SQL 流量时,基于 sp 的数据访问会产生 很多 交通量较少。当您监控流量以进行分析和分析时,这一点变得很重要 - 日志将变得更小且可读。

我不太喜欢存储过程,但我在一种情况下使用它们:

当查询相当大时,最好将其作为存储过程存储在数据库中,而不是从代码中发送。这样,就不必从应用程序服务器向数据库发送大量字符串字符,而只需 "EXEC SPNAME" 将发送命令。

当数据库服务器和 Web 服务器不在同一网络(例如,互联网通信)时,这就太过分了。即使情况并非如此,太大的压力也意味着大量带宽的浪费。

但是伙计,他们太难管理了。我尽可能地避开他们。

SQL存储过程不会提高查询的性能

显然,与在代码中构造 SQL 相比,使用存储过程有几个优点。

  1. 您的代码实现和 SQL 变得彼此独立。
  2. 代码更容易阅读。
  3. 一次编写多次使用。
  4. 修改一次
  5. 无需向程序员提供有关数据库的内部详细信息。等等等等

存储过程是 更多的 可维护,因为:

  • 每当您想要更改某些 SQL 时,无需重新编译 C# 应用程序
  • 您最终会重用 SQL 代码。

代码重复是 最糟糕的 当您尝试构建可维护的应用程序时您可以做的事情!

当您发现多处逻辑错误需要纠正时会发生什么?您更容易忘记更改复制和粘贴代码的最后一个位置。

在我看来,性能和安全性的提升是一个额外的优势。 您仍然可以编写不安全/低效的 SQL 存储过程。

更容易移植到另一个数据库 - 无需移植

编写所有存储过程的脚本以便在另一个数据库中创建并不困难。事实上——它是 更轻松 而不是导出表,因为无需担心主键/外键。

@Terrapin - 存储过程同样容易受到注入攻击。就像我说的:

始终对所有查询进行参数化 - 永远不要从用户输入中内联某些内容,这样就可以了。

这适用于存储过程和动态 Sql。

我不确定不重新编译您的应用程序是一个优势。我的意思是,无论如何,在再次上线之前,您已经针对该代码(应用程序和数据库)运行了单元测试。


@Guy - 是的,你是对的,存储过程确实允许您控制应用程序用户,以便他们只能执行存储过程,而不能执行底层操作。

我的问题是:如果所有的人都通过您的应用程序访问它,使用连接和具有有限更新/插入权限的用户等,这个额外的级别是否会增加安全性或额外的管理?

我的意见很大程度上是后者。如果他们已经破坏了您的应用程序,以至于可以重写它,那么他们可以使用大量其他攻击。

如果这些存储过程动态内联代码,仍然可以对它们执行 SQL 注入,因此黄金规则仍然适用,所有用户输入必须始终进行参数化。

到目前为止我还没有看到提到过的事情:最了解数据库的人并不总是编写应用程序代码的人。存储过程为数据库人员提供了一种与不想了解太多 SQL 知识的程序员进行交互的方法。大型(尤其是遗留)数据库并不是最容易完全理解的东西,因此程序员可能更喜欢一个简单的界面来满足他们的需要:让 DBA 弄清楚如何连接 17 个表来实现这一目标。

话虽这么说,用于编写存储过程的语言(PL/SQL 是一个臭名昭著的例子)是相当残酷的。它们通常不提供您在当今流行的命令式、OOP 或函数式语言中看到的任何细节。想想 COBOL。

因此,坚持使用仅抽象关系细节而不是包含业务逻辑的存储过程。

我通常编写 OO 代码。我怀疑你们大多数人也可能这样做。在这种情况下,对我来说显然所有业务逻辑(包括 SQL 查询)都属于类定义。拆分逻辑,使其一部分驻留在对象模型中,一部分驻留在数据库中,并不比将业务逻辑放入用户界面中更好。

关于存储过程的安全优势,前面的答案已经说了很多。这些分为两大类:

1) 限制对数据的直接访问。在某些情况下这绝对很重要,当您遇到这种情况时,存储过程几乎是您唯一的选择。然而,根据我的经验,这种情况只是例外,而不是规则。

2)SQL注入/参数化查询。这种反对意见是转移注意力的。内联 SQL - 甚至是动态生成的内联 SQL - 可以像任何存储过程一样完全参数化,并且可以用任何有价值的现代语言轻松完成。这两种方式都没有优势。(“懒惰的开发人员可能不会费心使用参数”并不是一个有效的反对意见。如果您团队中的开发人员更喜欢将用户数据连接到他们的 SQL 中而不是使用参数,那么您首先尝试教育他们,然后如果这不起作用,就解雇他们,就像您对拥有任何其他技术的开发人员所做的那样坏的、明显有害的习惯。)

我是 SPROC 代码的大力支持者。第一个原因是保持代码紧密耦合,其次是易于源代码控制,无需大量自定义实用程序来引入它。

在我们的 DAL 中,如果我们有非常复杂的 SQL 语句,我们通常将它们作为资源文件包含并根据需要更新它们(这也可以是一个单独的程序集,并按数据库交换,等等......)。

这使我们的代码和 sql 调用存储在同一版本控制中,而不会“忘记”运行一些外部应用程序进行更新。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top