将SQL保留在存储的Procs与代码中有什么优缺点?


274

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

到目前为止,我有:

代码的优点:

  • 易于维护-无需运行SQL脚本即可更新查询
  • 移植到另一个数据库更容易-没有proc移植到端口

存储过程的优点:

  • 性能
  • 安全

50
您也可能会争辩说,存储过程使维护更加容易-您不需要重新部署整个应用程序,只需更改一个查询即可。
达伦·高斯贝尔

27
@GvS:这是您公司的功能失调,而不是最佳实践。当然,在1处进行更改比在1000处更改更容易。DBA只是在尽自己的职责来防止对系统进行大刀阔斧的更改,因此应予以尊重。
Jeremy Holovacs 2011年

Answers:


179

我不喜欢存储过程

存储过程更具可维护性,因为:*每当您想更改某些SQL时,您都不必重新编译C#应用程序

无论如何,当数据类型改变,或者想要返回额外的列或其他内容时,最终还是要重新编译它。总体上,您可以从应用程序下方“透明地”更改SQL的次数很小

  • 您最终将重用SQL代码。

包括C#在内的编程语言具有这种令人惊奇的东西,称为函数。这意味着您可以从多个位置调用同一代码块!惊人!然后,您可以将可重用的SQL代码放入其中之一,或者,如果您想获得真正的高科技,则可以使用为您提供帮助的库。我相信它们被称为“对象关系映射器”,并且如今非常普遍。

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

同意,这就是为什么storedprocs是一件坏事的原因。与将代码重构为SQL块相比,将代码重构和分解(分解为较小的部分)要容易得多。

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

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

那么,推送1个新的proc还是4个新的Web服务器?

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

代码审查更容易。

你能解释一下吗?我不明白 特别是因为存储库可能不在源代码控制中,因此无法通过基于Web的SCM浏览器等进行访问。

更多缺点:

Storedprocs存在于数据库中,该数据库在外部显示为黑匣子。想要将它们放入源代码控制之类的简单事情变成了噩梦。

还有纯粹的问题。如果您想向CEO证明为什么要花700万美元建立一些论坛是合理的,那么将所有内容分解为一百万层可能是有意义的,但是否则,为每件小事创建一个storageproc就是无用的驴子工作。效益。


99

当前正在其他几个线程上对此进行讨论。我一直是存储过程的支持者,尽管已经提出了一些有关Linq to Sql的很好的论据。

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

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

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


47

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

我完全反对在3层环境中使用SP的情况,在3层环境中您有一个应用服务器在前。在这种环境中,您的应用程序服务器可以在其中运行您的业务逻辑。如果您另外使用SP,则会开始在整个系统上分布业务逻辑的实现,并且将变得非常不清楚,由谁来负责。最终,您将获得一个应用程序服务器,该服务器基本上只执行以下操作:

(Pseudocode)

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

因此,最终,您的中间层将在这个非常酷的4服务器集群上运行,每个集群都配备16个CPU,实际上它什么都不做!真是浪费!

如果您有一个胖gui客户端直接连接到您的数据库或什至更多的应用程序,那就另当别论了。在这种情况下,SP可以充当某种伪中间层,使您的应用程序与数据模型脱钩,并提供可控制的访问。


44

代码的优点:

  • 易于维护-无需运行SQL脚本即可更新查询
  • 移植到另一个数据库更容易-没有proc移植到端口

实际上,我认为您对此感到倒退。恕我直言,代码中的SQL很难维护,因为:

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

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

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


33

骗子

我发现,在扩展行为时,在存储过程中进行大量处理会使DB服务器变得僵硬。

但是,如果您有多个运行代码的服务器,那么与sql-server相比,在程序中进行所有此类操作 可能会使您扩展更多。当然,这不适用于仅进行常规获取或更新的存储过程,而不适用于执行更多处理(如遍历数据集)的存储过程。

优点

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

22

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

存储过程的更多优点:

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

16

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

我对数据模型的紧密耦合并没有太大的问题,因为我看不到有什么可能真正打破这种耦合。应用程序及其数据是固有耦合的。


13

存储过程。

如果错误失误或逻辑有所改变,则不必重新编译项目。另外,它还允许从不同的源进行访问,而不仅仅是在项目中对查询进行编码的地方。

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


24
如果发现自己为避免重新编译代码而做出基本的体系结构决策,那么在进行任何操作之前,请建立不会完全糟透的构建过程。这是一个非参数。
Michael Borgwardt

13

存储过程的优点

代码审查更容易。

耦合较少,因此更易于测试。

更容易调整。

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

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

如果还涉及其他服务(例如Reporting Services),则可能会发现将所有逻辑存储在存储过程中而不是代码中会变得更加容易,并且必须对其进行复制

缺点:

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


是的,您可以在Visual Studio 2012数据库项目和tfs中的存储过程和数据库中拥有版本控制。
hajirazin13年

11

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

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

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


1
不知道为什么这个答案被否决了..的确,较小的查询可以实现更好的查询。SQL Server团队甚至对此发表了评论。
布兰农

9

我喜欢存储过程,不知道我能够使用存储过程对应用程序进行更改的次数,该过程不会导致应用程序停机。

Transact SQL的忠实拥护者,调优大型查询对我来说非常有用。大约6年没有写任何内联SQL了!


我只是无法理解在代码中使用查询的另一种观点,只是无法理解...
Chaki_Black

8

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

性能-并非如此。在Sql 2000或更高版本中,查询计划的优化非常好,并且可以进行缓存。我确信Oracle等会做类似的事情。我认为不再需要使用存储过程来提高性能。

安全?为什么存储过程会更安全?除非您有一个非常不安全的数据库,否则所有访问都将来自您的DBA或您的应用程序。始终参数化所有查询-切勿内联来自用户输入的内容,您会很好的。

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

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


临时SQL执行计划仅在特定情况下重用:tinyurl.com/6x5lmd [带有代码证明的SO答案] LINQ to SQL正式失效:tinyurl.com/6298nd [博客文章]
HTTP 410

1
Procs是迄今为止最安全的方法。它们限制了用户仅在proc中执行操作。他们不能直接进入表或视图,他们对这些对象具有写权限并可以进行更改。必须采取措施来防止内部威胁造成的伤害。
HLGEM 2011年

8

@基思

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

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

(当然,执行存储过程可能会为他们提供所需的所有数据,但这取决于可用的存储过程。授予他们对表的访问权限使他们可以访问所有内容。)


1
@Joe Philllips,视图不能为您提供更好的保护,甚至不能等同于proc的安全性,并且它们对于防止欺诈或内部伤害没有用。当您使用proc时,安全性模型是,他们的用户只能访问proc而不能访问表或视图,因此他们只能执行proc之外的任何操作。如果您有财务数据并且没有使用proc,则您的系统将面临风险。
HLGEM 2011年

7

这样想吧

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

我更喜欢存储过程

对proc进行性能测试也更容易,将其放入查询分析器中,将set io / time设置为io / time,将showplan_text设置为on并将结果设置为voila

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

只是我的2美分


6

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

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


6

最好地利用您的应用代码:处理逻辑。
使用您的数据库来了解数据库的最佳功能:存储数据。

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

另外,带有可选搜索参数的存储过程效率很低,因为您必须事先指定所有可能的参数,而有时则不可能进行复杂的搜索,因为您无法预测参数将在搜索中重复多少次。


2
当多个应用程序访问同一个数据库,并且数据库受到导入和其他直接访问的影响(将所有价格更新10%)时,逻辑必须存在于数据库中,否则您将失去数据库完整性。
HLGEM

对于多个应用程序,将逻辑放入所有应用程序都使用的库中可以保持完整性,同时仍将逻辑保持在应用程序语言中。对于导入/直接访问,通常应视情况决定是否应执行适用于应用程序的规则。
戴夫·谢罗曼

3
多个应用程序不应对数据库进行相同类型的更改。应该有一个应用程序组件可以处理单一类型的更改。然后,如果其他人感兴趣,该应用程序应公开服务。多个应用以他们认为合适的方式更改同一数据库/表的原因是导致应用系统和数据库变得不可维护。
Jiho Han 2010年

2
“应该有一个应用程序组件来处理单一类型的更改” –该组件可以是存储过程,例如在PL / SQL中。
罗素H

6

在安全性方面,存储过程更加安全。有些人认为所有访问都将通过应用程序进行。许多人忘记的是,大多数安全漏洞来自公司内部。考虑一下有多少开发人员知道您的应用程序的“隐藏”用户名和密码?

而且,正如MatthieuF所指出的,由于应用程序(无论是在台式机还是Web服务器上)与数据库服务器之间的往返次数减少,因此可以大大提高性能。

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

我还将在plus列中进行检查,以将SQL编程交到专门从事SQL编程的人员手中,并使SP简化隔离和测试/优化代码。

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


当模型更改时,通常无论代码是使用sproc还是动态sql,代码也都需要更改。创建表格/模式后,您多久更改一次表格/模式?不经常。通常,更改来自需要添加另一列或另一张表的业务,在这种情况下,我怀疑您可以在不更改代码的情况下进行操作。
Jiho Han 2010年

4

我参加的Microsoft TechEd安全会议的一项建议是,通过存储的procs进行所有调用,并拒绝直接访问表。据称这种方法可提供额外的安全性。我不确定仅出于安全性考虑是否值得,但是如果您已经在使用存储的proc,就不会感到伤害。


当您特别关注个人信息或财务信息时,数据安全性很重要。大多数欺诈行为是由内部人员实施的。您不想授予他们绕过内部控制所需的访问权限。
HLGEM

4

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


4

我坚决支持存储过程,假设您不作弊并在存储过程中使用动态SQL。首先,使用存储的proc允许dba在存储的proc级别而不是表级别设置权限。这不仅对打击SQL注入行为至关重要,而且对于防止内部人员直接访问数据库和更改事物也至关重要。这是防止欺诈的一种方法。除非通过繁琐的程序,否则不得访问包含个人信息(SSN,信用卡号等)或以任何方式产生财务交易的数据库。如果您使用任何其他方法,则将数据库开放给公司中的个人以创建伪造的财务交易或窃取可用于身份盗用的数据。

与从应用程序发送的SQL相比,存储的proc也更易于维护和性能调整。它们还使dba能够查看数据库结构更改对数据访问方式的影响。我从未见过一个好的dba,它允许动态访问数据库。


4

我们现在在工作的Oracle DB上使用存储过程。我们还使用Subversion。所有存储过程都将创建为.pkb和.pks文件,并保存在Subversion中。我之前已经做过内联SQL,这很痛苦!我非常喜欢我们在这里做的方式。创建和测试新的存储过程比在代码中轻松得多。

有个


3

较小的原木

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


3

我不是存储过程的忠实拥护者,但是在一种情况下使用它们:

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

当数据库服务器和Web服务器不在同一网络上(例如,Internet通信)时,这是多余的。即使不是这种情况,压力太大也意味着大量浪费带宽。

但是,伙计们,他们管理起来是如此糟糕。我会尽量避免他们。



3

很显然,与在代码中构造SQL相比,使用存储过程具有多个优势。

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

我从来没有遇到过这样的情况:对代码问题或新功能的了解减少了对我的好处,您能说清楚数字5吗?
OpenCoderX

2

存储过程更具可维护性,因为:

  • 每当您想更改一些SQL时,都不必重新编译C#应用程序
  • 您最终将重用SQL代码。

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

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

我认为,性能和安全性方面的收益是额外的优势。您仍然可以编写不安全/效率低下的SQL存储过程。

移植到另一个数据库更容易-没有proc移植到端口

用脚本编写所有存储过程以在另一个数据库中创建并不是很困难。实际上- 因为没有主键/外键值得担心,所以比导出表更容易


请注意:“更容易移植到另一个数据库-无需移植任何程序”是指移植到另一个DBMS,而不仅仅是移植到另一个安装。您知道;-)。
sleske

2

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

始终参数化所有查询-切勿内联来自用户输入的内容,您会很好的。

这适用于proc和动态Sql。

我不确定不重新编译您的应用是否有好处。我的意思是,您已经针对该代码(应用程序和数据库)运行了单元测试,然后仍然再次上线。


@Guy-是的,没错,存储过程确实可以控制应用程序用户,以便他们只能执行存储过程,而不能执行基础操作。

我的问题是:如果通过您的应用程序进行的所有访问(使用连接和权限有限的用户来更新/插入等),此额外级别是否会增加安全性或额外的管理?

我的看法是后者。如果他们破坏了您的应用程序,可以重新编写应用程序,则他们可以使用许多其他攻击。

如果它们动态地内联代码,则仍然可以针对那些sproc执行sql注入,因此黄金法则仍然适用,所有用户输入都必须始终参数化。


这不仅是您需要战斗的外部攻击。您不能允许用户直接访问表,然后用户可以更改数据以进行欺诈。
HLGEM

作为一项策略,不应允许存储的proc使用动态sql,如果您要查找的话,几乎总是有非动态解决方案。
HLGEM

SQL注入对于使用动态内联代码的sproc不太有效,因为动态代码是在调用者权限而不是所有者权限下执行的(不同于静态代码)。对于SQL Server而言,这是正确的-对Oracle不确定。
HTTP 410

2

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

话虽这么说,用于编写存储过程的语言(PL / SQL是一个臭名昭著的示例)非常残酷。它们通常不提供您在当今流行的命令式,OOP或功能性语言中看到的任何精美信息。想想COBOL。

因此,请坚持只提取关系细节而不是包含业务逻辑的存储过程。


“用于编写存储过程的语言(PL / SQL是一个臭名昭著的示例)非常残酷,并且没有提供您在当今流行的语言中看到的任何精美信息。” 您需要重新阅读PL / SQL文档(download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/toc.htm)。PL / SQL具有使用程序包的封装,通过对象类型的OOP,异常处理,代码的动态执行,调试器,分析器等,以及数百种由Oracle提供的标准程序包/库,用于执行从HTTP调用到加密和正则表达式的所有操作。PL / SQL有很多优点。
2009年

2

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

在较早的答案中,关于存储过程的安全性好处已经说了很多。这些分为两大类:

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

2)SQL注入/参数化查询。这个异议是一个红鲱鱼。内联SQL-甚至是动态生成的内联SQL-都可以像任何存储的proc一样完全参数化,并且可以用任何值得赞扬的现代语言轻松地完成。这两种方式都没有优势。(“懒惰的开发人员可能不会打扰使用参数”不是有效的反对意见。如果您的团队中有一些开发人员只希望将用户数据连接到他们的SQL中而不是使用参数,则首先尝试对其进行教育,然后将其解雇如果那行不通,就像您和其他具有不良,明显有害习惯的开发人员一样。)


2

我是SPROC的代码的大力支持者。原因之一是保持代码紧密耦合,其次是源代码控制的简便性,而无需使用大量自定义工具。

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

这样可以将我们的代码和sql调用存储在同一版本控制中,而无需“忘记”运行某些外部应用程序进行更新。


当您“忘记”将更改复制到表时该怎么办?
craigmoliver

流程可以解决部署问题。
汤姆·安德森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.