我们有一个用C#编写的应用程序,它连接到ms sql server。 我们使用为每个数据库调用创建一个存储过程,但后来我们注意到使用存储过程给我们带来了很大的缺点,如果我们更改数据库,我们不知道需要更新哪些存储过程。
现在我想知道使用存储过程是坏事还是好事?
答案 0 :(得分:23)
多年来,存储过程已经失宠了。目前访问关系数据库的首选方法是通过一个O / R映射器,如NHibernate或Entity Framework。
存储过程需要 更多的工作来开发和维护。对于每个表,您必须编写单个存储过程来创建,检索,更新和删除行,以及为您要进行的每个不同查询添加单独的存储过程。最重要的是,您必须在代码中编写类和/或方法来调用每个存储过程。将其与O / R映射器进行比较:您需要编写的只是类定义,数据库表和映射文件。事实上,现代ORM使用基于约定的方法,无需单独的映射定义。
存储过程会促进不良开发实践,特别是它们要求您违反DRY(不要重复自己),因为您必须在数据库表中输入六次或更多次的字段列表< em>至少。如果您需要向数据库表添加单个列,这将是一个巨大的痛苦。将对象作为参数传递给存储过程是不可能的,只有简单的类型(字符串,整数,日期/时间等)使得几乎不可能避免使用大量的参数列表(十几个或更多)。
存储过程会促进错误的配置管理实践。这源于DBA应该能够独立于代码本身修改它们的论点。这样做会导致生成的代码版本从未经过集成测试,与源代码管理中的单个特定修订版本不对应,实际上甚至可能与源代码中的任何修订版不对应完全控制。基本上,如果您没有可审计的记录,端到端,您的代码的哪个版本正在生产中,那么您将遇到麻烦。
存储过程必须与代码主体分开部署。除非您有一个完全自动化的流程来更新它们,否则它们会在一个或多个环境中与主代码库不同步,从而引发错误的风险显着增加。如果您需要使用源代码控制的bisect工具来跟踪引入错误的修订版,那么这尤其成问题。
存储过程不灵活。如果您想以几种不同的方式查询数据(不同的排序顺序,懒惰与急切加载,分页等),您需要为所有不同的用例编写大量单独的存储过程,而ORM为您提供灵活的,强大的查询语言(例如Linq to NHibernate)。
存储过程要求您重新发明轮子。如果您需要乐观并发,或工作单元模式,延迟加载,或身份映射,或父/子集合的处理,或缓存,或类层次结构映射,或几乎您阅读的任何其他设计模式在Martin Fowler的书“企业应用程序架构模式”中,您需要自己从头开始重建此功能,而O / R映射器为您提供所有这些,甚至更多,直接开箱即用。很多时候,你最终会使用复制和粘贴代码重新发明这些轮子,这也是一种不好的做法。
存储过程很难进行单元测试。使用ORM,您可以模拟数据库代码,以便能够快速测试业务逻辑。使用存储过程,您必须从头开始重建整个测试数据库。
存储过程不会带来任何性能优势。通过线路传递sproc的名称而不是SQL字符串所获得的(微小)增益很容易被这样一个事实所抵消,即你很可能最终使用相同的程序调用相同的程序两到三次同一个请求中的参数,而ORM会在其身份图中查看并说:“嘿,我已经检索过那个,根本不需要再往返一次。”此外,声称存储过程缓存在服务器上,而ad-hoc SQL不是,这是Frans Bouma在他的博客文章“Stored Procedures are bad, m'kay?”
存储过程在安全性方面提供很少或没有优势,并且不会保护您免受SQL注入漏洞的影响。例证:
create procedure GetUsers(@SortOrder nvarchar(50))
as
begin
declare @sql nvarchar(100)
set @sql = 'SELECT * FROM Users ORDER BY ' + @SortOrder
exec @sql
end
当然,您可以编写没有SQL注入漏洞的存储过程,但是您可以通过使用参数化查询在业务层中编写没有SQL注入漏洞的临时SQL。将针对SQL注入的保护归因于存储过程,而不是将SQL字符串粉碎在一起,这是一个红色的鲱鱼并且完全具有误导性。
答案 1 :(得分:6)
这不是SP问题,这是您的开发过程中的一个问题。如果您没有所需的信息 - 只需获取它。
您可以创建一个简单的可视化地图,显示您的表架构和相关SP。如果您的数据库太大而无法进行可视化映射,请添加由SP组成的公共文本文件及其所依赖的表的名称。
无论如何,数据库越大,将模式的详细信息内联到应用程序代码中的工作就越糟糕。当您使用SP时,您可以保证此功能不会加倍,并且大多数更改将在数据库端发生,而无需应用程序重新编译和重新分发。
<强> UPD 强>
我忘了提一件事。良好的数据库工具提供了查找每个SP的相关表的简便方法。例如,在Microsoft SQL Management Studio的SP上下文菜单中有“查看依赖项”项。
答案 2 :(得分:5)
我认为SP适用于数据库中的计算/数据操作/报告数据源。
当它仅用于表格行的数据检索/更新时,你会遇到一个受伤的整个世界。
这是一些数据访问层遵循的方法,单个行的数据检索sps可能会变得很痛苦。
所以不,我不会推荐这是最好的方式。
答案 3 :(得分:2)
有2个观点,有些人说他们是邪恶的,有些人则发誓。我对此采取了中间道路观点。
赞成
可维护性,如果您需要稍微更改查询而不会实际影响其他代码,则可以执行此操作而无需推出新程序集
安全性,没有SQL注入攻击,除非您在proc
缺点
没有文档和标准,事情就会迅速失控,并使数据库维护成为一场噩梦。
建议
使用它们进行报告,以及进行更高级的数据库操作,但尝试避免简单的CRUD操作
将您的业务逻辑保留在数据库之外,该数据库应位于单独的层IMHO中。
答案 4 :(得分:1)
存储过程对于在数据库级别强制执行约束非常有用。验证限制访问数据库的少数存储过程比验证客户端代码的每一部分更容易。所以这使他们很好。
除此之外,我是一个怀疑论者。我喜欢把所有东西放在一个地方,用我可以单独测试的语言。
答案 5 :(得分:1)
你不能说这是好事还是坏事。它们有优点和缺点,根据项目的不同,它们的重量也可能不同。
一些优点:
一些缺点:
答案 6 :(得分:1)
我参与了很多使用存储过程的项目。基本上,业务层已移至数据库,因为团队负责人对他在上一份工作中遇到的一些神谕大师印象深刻。
存储过程代码比C#(在Visual Studio中)更难维护,因为工具更糟糕,调试更难等。
同时,拥有清晰的数据规则接口。考虑将在数据库上进行哪些查询可能是一件好事。
尝试在源代码管理中保留数据库生成和迁移(更新)代码。如果您真的需要,请在那里包含存储过程。保持存储过程逻辑尽可能简单(不要做任何业务逻辑,只是一致性样式的东西)。甚至可能从更抽象的表示(以及调用它们的C#代码)生成它们。
答案 7 :(得分:1)
您不知道数据库架构更改是否会影响SP。这意味着更改数据库的团队不会编写SP。在这种情况下,从SP转向内联SQL或ORM对您没有帮助。而不是检查SP,你将不得不检查你的代码。我建议你购买好的工具,告诉你表和SP之间的依赖关系。
答案 8 :(得分:0)
这就是为什么你需要良好的文档和良好的DBA来编写这样的软件。
恕我直言存储过程也不错,它们可用于许多有用的东西,如触发器,或执行一些复杂的查询,而你必须在客户端编写许多查询。但当然没有什么是好的。我发现的一些缺点:存储过程可能会导致服务器端的更多工作(有时可能会移动到客户端),有时它们很难维护。
但另一方面,当有一天你必须向一些编写软件的程序员提供对数据库的访问时,它们非常有用。 Java将无法使用您在C#中编写的所有这些db类。在这种情况下,最好在数据库中包含一些逻辑,这样无论使用何种客户端或语言,都可以使用它。
答案 9 :(得分:0)
存储过程非常适合非常常见且不会经常更改的查询。如果你有一个“getname”的SP总是从表中提取名字和名字,那么从长远来看这将是一个很好的。此外,如果您有一个非常复杂的查询,可能会在客户端结束大量的马力,存储过程将有所帮助。
任何可能是动态的查询都不应该是SP。如果它是经常变化的东西,或者你需要快速访问的东西,制作SP是个坏主意。这就是原因。假设您构建了一个获得特定类型数据的漂亮SP。您有3个不同的项目使用它。但是你需要一些不同的东西,所以你的选择是:
总而言之,存储过程非常适合某些需求,但不适合其他需求。评估您的需求可能会发生多大变化,或者使用标准查询的缺点是什么。
答案 10 :(得分:0)
存储过程的另一大优势是您可以动态地对后端进行更改,而无需重新部署应用程序(只要原型不会更改)。
在我工作的大公司,代码部署是一项主要工作,需要至少30天和多次批准。几乎可以立即对DB进行更改。
最后,请记住,存储过程还可以提供针对恶意程序员的保护。有一个伟大的DBA,但最便宜的承包商团队编写您的代码? DBA可以编写存储过程,然后从表中删除DML权限,强制代码通过存储过程进行任何更改。这样,你就不必担心有些人会在代码中放入一些SQL而不小心消除了一半的数据库。
答案 11 :(得分:-3)
存储程序通常是件好事: