将SQL保留在C#源代码或存储的Procs中有哪些优点/缺点?我一直在和一个朋友在我们正在研究的一个开源项目(C#ASP.NET论坛)上讨论这个问题。目前,大多数数据库访问是通过在C#中构建SQL内联并调用SQL Server DB来完成的。因此,我正在尝试确定,对于该特定项目,哪一个最好。
到目前为止,我有:
代码的优点:
- 易于维护-无需运行SQL脚本即可更新查询
- 移植到另一个数据库更容易-没有proc移植到端口
存储过程的优点:
- 性能
- 安全
将SQL保留在C#源代码或存储的Procs中有哪些优点/缺点?我一直在和一个朋友在我们正在研究的一个开源项目(C#ASP.NET论坛)上讨论这个问题。目前,大多数数据库访问是通过在C#中构建SQL内联并调用SQL Server DB来完成的。因此,我正在尝试确定,对于该特定项目,哪一个最好。
到目前为止,我有:
代码的优点:
存储过程的优点:
Answers:
我不喜欢存储过程
存储过程更具可维护性,因为:*每当您想更改某些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就是无用的驴子工作。效益。
当前正在其他几个线程上对此进行讨论。我一直是存储过程的支持者,尽管已经提出了一些有关Linq to Sql的很好的论据。
将查询嵌入代码中可以使您紧密地与数据模型耦合。存储过程是契约式编程的一种好形式,这意味着只要维护以存储过程的输入和输出表示的契约,DBA即可自由更改过程中的数据模型和代码。
当查询隐藏在代码中而不是集中在一个易于管理的中央位置时,调整生产数据库可能非常困难。
[编辑]这是另一个当前的讨论
我认为您不能就这个问题投赞成票或反对票。这完全取决于您的应用程序的设计。
我完全反对在3层环境中使用SP的情况,在3层环境中您有一个应用服务器在前。在这种环境中,您的应用程序服务器可以在其中运行您的业务逻辑。如果您另外使用SP,则会开始在整个系统上分布业务逻辑的实现,并且将变得非常不清楚,由谁来负责。最终,您将获得一个应用程序服务器,该服务器基本上只执行以下操作:
(Pseudocode)
Function createOrder(Order yourOrder)
Begin
Call SP_createOrder(yourOrder)
End
因此,最终,您的中间层将在这个非常酷的4服务器集群上运行,每个集群都配备16个CPU,实际上它什么都不做!真是浪费!
如果您有一个胖gui客户端直接连接到您的数据库或什至更多的应用程序,那就另当别论了。在这种情况下,SP可以充当某种伪中间层,使您的应用程序与数据模型脱钩,并提供可控制的访问。
代码的优点:
- 易于维护-无需运行SQL脚本即可更新查询
- 移植到另一个数据库更容易-没有proc移植到端口
实际上,我认为您对此感到倒退。恕我直言,代码中的SQL很难维护,因为:
将存储过程视为从数据库对象调用的方法-它们更易于重用,只有一个地方可以编辑,并且如果您更改了数据库提供程序,则更改将在存储过程中而不是代码中发生。
也就是说,正如Stu在我之前所说的那样,存储过程的性能提升是最小的,您还不能在存储过程中设置断点(尚未)。
骗子
我发现,在扩展行为时,在存储过程中进行大量处理会使DB服务器变得僵硬。
但是,如果您有多个运行代码的服务器,那么与sql-server相比,在程序中进行所有此类操作 可能会使您扩展更多。当然,这不适用于仅进行常规获取或更新的存储过程,而不适用于执行更多处理(如遍历数据集)的存储过程。
优点
存储过程。
如果错误失误或逻辑有所改变,则不必重新编译项目。另外,它还允许从不同的源进行访问,而不仅仅是在项目中对查询进行编码的地方。
我认为维护存储过程并不困难,您不应该直接在数据库中对它们进行编码,而应该先在单独的文件中进行编码,然后就可以在需要设置的任何DB上运行它们。
存储过程的优点:
代码审查更容易。
耦合较少,因此更易于测试。
更容易调整。
从网络流量的角度来看,性能通常会更好-如果有游标或类似游标,则不会多次访问数据库
您可以更轻松地保护对数据的访问,删除对表的直接访问,通过proc加强安全性-这还使您可以相对迅速地找到更新表的任何代码。
如果还涉及其他服务(例如Reporting Services),则可能会发现将所有逻辑存储在存储过程中而不是代码中会变得更加容易,并且必须对其进行复制
缺点:
对于开发人员而言,更难管理:脚本的版本控制:每个人都有自己的数据库吗,版本控制系统是否与数据库和IDE集成在一起?
在某些情况下,用代码动态创建的sql比存储的proc具有更好的性能。如果您创建了一个存储的proc(比如说sp_customersearch),因为它必须非常灵活,它会因数十个参数而变得极为复杂,那么您可能会在运行时在代码中生成一个简单得多的sql语句。
有人可能会争辩说,这只是将一些处理从SQL移到了Web服务器,但是总的来说这将是一件好事。
这项技术的另一个优点是,如果您正在SQL事件探查器中查找,则可以看到生成的查询并对其进行调试,这比看到带有20个参数的存储的proc调用要容易得多。
我喜欢存储过程,不知道我能够使用存储过程对应用程序进行更改的次数,该过程不会导致应用程序停机。
Transact SQL的忠实拥护者,调优大型查询对我来说非常有用。大约6年没有写任何内联SQL了!
您列出了存储过程的两个优点:
性能-并非如此。在Sql 2000或更高版本中,查询计划的优化非常好,并且可以进行缓存。我确信Oracle等会做类似的事情。我认为不再需要使用存储过程来提高性能。
安全?为什么存储过程会更安全?除非您有一个非常不安全的数据库,否则所有访问都将来自您的DBA或您的应用程序。始终参数化所有查询-切勿内联来自用户输入的内容,您会很好的。
无论如何,这是性能的最佳实践。
Linq绝对是我现在进行新项目的方式。看到类似的帖子。
@基思
安全?为什么存储过程会更安全?
正如Komradekatz建议的那样,您可以禁止访问表(对于连接到DB的用户名/密码组合),并且仅允许SP访问。这样,如果有人获得了数据库的用户名和密码,他们就可以执行SP,但是不能访问表或数据库的任何其他部分。
(当然,执行存储过程可能会为他们提供所需的所有数据,但这取决于可用的存储过程。授予他们对表的访问权限使他们可以访问所有内容。)
最好地利用您的应用代码:处理逻辑。
使用您的数据库来了解数据库的最佳功能:存储数据。
您可以调试存储过程,但是会发现更容易调试和维护代码中的逻辑。通常,每次更改数据库模型时,您将结束重新编译代码。
另外,带有可选搜索参数的存储过程效率很低,因为您必须事先指定所有可能的参数,而有时则不可能进行复杂的搜索,因为您无法预测参数将在搜索中重复多少次。
在安全性方面,存储过程更加安全。有些人认为所有访问都将通过应用程序进行。许多人忘记的是,大多数安全漏洞来自公司内部。考虑一下有多少开发人员知道您的应用程序的“隐藏”用户名和密码?
而且,正如MatthieuF所指出的,由于应用程序(无论是在台式机还是Web服务器上)与数据库服务器之间的往返次数减少,因此可以大大提高性能。
以我的经验,通过存储过程抽象数据模型也极大地提高了可维护性。作为过去不得不维护许多数据库的人,当面对所需的模型更改以能够简单地更改一个或两个存储过程并使更改对于所有外部应用程序完全透明时,这是一种放松。很多时候,您的应用程序并不是唯一指向数据库的应用程序-还有其他应用程序,报告解决方案等。因此,跟踪所有这些受影响的点可能会带来对表开放访问的麻烦。
我还将在plus列中进行检查,以将SQL编程交到专门从事SQL编程的人员手中,并使SP简化隔离和测试/优化代码。
我看到的一个缺点是,许多语言不允许传递表参数,因此传递未知数的数据值可能会很烦人,并且某些语言仍无法处理从单个存储过程中检索多个结果集的问题(尽管在这方面,后者不会使SP比内联SQL差。
我参加的Microsoft TechEd安全会议的一项建议是,通过存储的procs进行所有调用,并拒绝直接访问表。据称这种方法可提供额外的安全性。我不确定仅出于安全性考虑是否值得,但是如果您已经在使用存储的proc,就不会感到伤害。
我坚决支持存储过程,假设您不作弊并在存储过程中使用动态SQL。首先,使用存储的proc允许dba在存储的proc级别而不是表级别设置权限。这不仅对打击SQL注入行为至关重要,而且对于防止内部人员直接访问数据库和更改事物也至关重要。这是防止欺诈的一种方法。除非通过繁琐的程序,否则不得访问包含个人信息(SSN,信用卡号等)或以任何方式产生财务交易的数据库。如果您使用任何其他方法,则将数据库开放给公司中的个人以创建伪造的财务交易或窃取可用于身份盗用的数据。
与从应用程序发送的SQL相比,存储的proc也更易于维护和性能调整。它们还使dba能够查看数据库结构更改对数据访问方式的影响。我从未见过一个好的dba,它允许动态访问数据库。
很显然,与在代码中构造SQL相比,使用存储过程具有多个优势。
存储过程更具可维护性,因为:
尝试构建可维护的应用程序时,代码重复是最糟糕的事情!
当您发现需要多次纠正的逻辑错误时,会发生什么情况?您更容易忘记更改复制和粘贴代码的最后一个位置。
我认为,性能和安全性方面的收益是额外的优势。您仍然可以编写不安全/效率低下的SQL存储过程。
移植到另一个数据库更容易-没有proc移植到端口
用脚本编写所有存储过程以在另一个数据库中创建并不是很困难。实际上- 因为没有主键/外键值得担心,所以比导出表更容易。
@Terrapin-存储过程同样容易受到注入攻击。就像我说的:
始终参数化所有查询-切勿内联来自用户输入的内容,您会很好的。
这适用于proc和动态Sql。
我不确定不重新编译您的应用是否有好处。我的意思是,您已经针对该代码(应用程序和数据库)运行了单元测试,然后仍然再次上线。
@Guy-是的,没错,存储过程确实可以控制应用程序用户,以便他们只能执行存储过程,而不能执行基础操作。
我的问题是:如果通过您的应用程序进行的所有访问(使用连接和权限有限的用户来更新/插入等),此额外级别是否会增加安全性或额外的管理?
我的看法是后者。如果他们破坏了您的应用程序,可以重新编写应用程序,则他们可以使用许多其他攻击。
如果它们动态地内联代码,则仍然可以针对那些sproc执行sql注入,因此黄金法则仍然适用,所有用户输入都必须始终参数化。
到目前为止,我还没有看到一些东西:最了解数据库的人并不总是写应用程序代码的人。存储过程为数据库人员提供了一种与真正不想学习太多有关SQL的程序员进行交互的方式。大型数据库(尤其是传统数据库)并不是最容易完全理解的事情,因此程序员可能更喜欢一个简单的界面来满足他们的需求:让DBA弄清楚如何联接17个表来实现这一点。
话虽这么说,用于编写存储过程的语言(PL / SQL是一个臭名昭著的示例)非常残酷。它们通常不提供您在当今流行的命令式,OOP或功能性语言中看到的任何精美信息。想想COBOL。
因此,请坚持只提取关系细节而不是包含业务逻辑的存储过程。
我通常写OO代码。我怀疑你们中的大多数人也可能这样做。在这种情况下,对我来说显而易见的是,所有业务逻辑(包括SQL查询)都属于类定义。拆分逻辑,使逻辑的一部分驻留在对象模型中,而另一部分驻留在数据库中,这比将业务逻辑放入用户界面更好。
在较早的答案中,关于存储过程的安全性好处已经说了很多。这些分为两大类:
1)限制直接访问数据。在某些情况下,这绝对是重要的,而且当您遇到一个问题时,存储的procs几乎是您唯一的选择。以我的经验,这种情况是例外而不是规则。
2)SQL注入/参数化查询。这个异议是一个红鲱鱼。内联SQL-甚至是动态生成的内联SQL-都可以像任何存储的proc一样完全参数化,并且可以用任何值得赞扬的现代语言轻松地完成。这两种方式都没有优势。(“懒惰的开发人员可能不会打扰使用参数”不是有效的反对意见。如果您的团队中有一些开发人员只希望将用户数据连接到他们的SQL中而不是使用参数,则首先尝试对其进行教育,然后将其解雇如果那行不通,就像您和其他具有不良,明显有害习惯的开发人员一样。)
我是SPROC的代码的大力支持者。原因之一是保持代码紧密耦合,其次是源代码控制的简便性,而无需使用大量自定义工具。
在我们的DAL中,如果我们有非常复杂的SQL语句,通常会将它们作为资源文件包含在内,并根据需要进行更新(这也可以是一个单独的程序集,并且可以按db换出,等等。)。
这样可以将我们的代码和sql调用存储在同一版本控制中,而无需“忘记”运行某些外部应用程序进行更新。