在存储过程中保留SQL与代码有什么优点和缺点
在您的C#源代码或Stored Procs中保留SQL的优点/缺点是什么? 我一直在和一位朋友讨论一个我们正在开发的开源项目(C#ASP.NET论坛)。 目前,大部分数据库访问都是通过在C#中内联SQL并调用SQL Server数据库来完成的。 所以我试图确定哪个对于这个特定的项目是最好的。
到目前为止我有:
代码中的优点:
- 更容易维护 – 不需要运行SQL脚本来更新查询
- 更容易移植到另一个数据库 – 没有过程端口
存储Procs的优点:
- 性能
- 安全
我不是存储过程的粉丝
存储过程更易于维护,因为:*您无需重新编译您的C#应用程序就可以随时更改某些SQL
当数据types发生变化,或者你想返回一个额外的列,或者其他什么的时候,你最终会重新编译它。 您可以“透明地”从应用程序底下更改SQL的次数总体上很小
- 你最终重用SQL代码。
编程语言,包括C#,有这个惊人的东西,称为函数。 这意味着你可以从多个地方调用相同的代码块! 惊人! 然后你可以把可重用的SQL代码放在其中一个,或者如果你想获得真正的高科技,你可以使用一个库来为你做。 我相信他们被称为对象关系映射器(Object Relational Mappers),而且这些日子相当普遍。
代码重复是您尝试构build可维护的应用程序时可以做的最糟糕的事情!
同意,这就是为什么storedprocs是一件坏事。 重构和分解(分成更小的部分)代码到SQL的function比SQL更容易?
你有4个Web服务器和一堆使用相同的SQL代码的Windows应用程序现在你意识到有一个SQl代码的小问题,所以你宁愿……改变一个地方的proc或将代码推送到所有网页服务器,重新安装所有的桌面应用程序(clickonce可能会帮助)所有的窗口框
为什么你的Windows应用程序直接连接到中央数据库? 这看起来就像是一个巨大的安全漏洞,还有瓶颈,因为它排除了服务器端的caching。 他们不应该通过Web服务或类似的连接到您的Web服务器吗?
那么推一个新的sproc,或者四个新的webservers?
在这种情况下,更容易推出一个新的sproc,但根据我的经验,95%的“推送更改”会影响代码而不影响数据库。 如果你将20件东西推到当月的Web服务器上,1件东西放到数据库中,那么如果你把21件东西推到Web服务器上,那么你几乎不会损失太多,而把数据归零。
更容易的代码审查。
你能解释一下吗? 我不明白 尤其是看到sprocs可能不在源代码控制之中,因此无法通过基于web的SCM浏览器访问等等。
更多的缺点:
存储过程存在于数据库中,这在外面显示为一个黑盒子。 简单的事情,如想要把他们在源代码控制成为一场噩梦。
也有纯粹的努力的问题。 如果你想向你的首席执行官辩解,为什么要花费700万美元来build立一些论坛,否则将所有事情分解成一百万层是合理的,但是否则为每一件小事创build一个存储过程只是额外的无用功效益。
目前正在讨论这个问题。 我是存储过程的一贯支持者,尽pipeLinq to Sql有一些很好的论点。
在您的代码中embedded查询可以将您紧紧地绑定到您的数据模型。 存储过程是合同编程的一种很好的forms,这意味着只要存储过程的input和输出代表的合同得到维护,DBA就可以自由地修改过程中的数据模型和代码。
当查询被隐藏在代码中而不是在一个集中的,易于pipe理的位置时,调整生产数据库可能会非常困难。
[编辑]这是另一个当前的讨论
在我看来,你不能在这个问题上投赞成票或不赞成。 这完全取决于你的应用程序的devise。
我完全投票反对在三层环境中使用SP,而在前面有一个应用程序服务器。 在这种环境中,您的应用程序服务器在那里运行您的业务逻辑。 如果您另外使用SP,则开始在整个系统中分发您的业务逻辑的实现,并且对谁负责是非常不清楚的。 最终你将会得到一个应用程序服务器,除了以下内容外,基本上什么也不做。
(Pseudocode) Function createOrder(Order yourOrder) Begin Call SP_createOrder(yourOrder) End
所以最后你的中间层运行在这个非常酷的4个服务器集群上,每个集群都配备了16个CPU,它实际上什么都不做! 真是太浪费了!
如果你有一个胖客户端直接连接到你的数据库或更多的应用程序,这是一个不同的故事。 在这种情况下,SP可以作为某种types的伪中间层,将应用程序从数据模型中分离出来,并提供可控访问。
代码中的优点:
- 更容易维护 – 不需要运行SQL脚本来更新查询
- 更容易移植到另一个数据库 – 没有过程端口
其实我觉得你倒退了 恕我直言,代码中的SQL是很难维护,因为:
- 你最终在相关的代码块中重复自己
- SQL在许多IDE中不被支持作为一种语言,所以你只有一系列未经检验的string为你执行任务
- 数据types,表名或约束的变化要比为整个数据库换一个新数据库更为普遍
- 随着查询复杂度的增加,您的难度将会增加
- 并testing内联查询需要构build项目
把存储过程看作是你从数据库对象中调用的方法 – 它们更容易重用,只有一个地方可以编辑,如果你改变数据库提供者,改变发生在你的存储过程中,而不是在你的代码中。
也就是说,存储过程的性能增益是微乎其微的,因为Stu在我之前说过,而且你不能在存储过程中放置一个断点。
CON
我发现在存储过程中进行大量的处理将会使您的数据库服务器变得不灵活。
但是,如果你有多个运行你的代码的服务器, 那么在你的程序中执行所有与sql服务器相反的操作, 可能会使你扩展更多。 当然,这并不适用于只能进行正常读取或更新的存储过程,而不适用于执行更多处理(如循环数据集)的过程。
PROS
- 性能为什么它可能是值得的(避免查询分析DB驱动程序/计划娱乐等)
- 数据操作没有embedded到C / C ++ / C#代码中,这意味着我的底层代码要less一些。 SQL在分开列出时不那么冗长,更容易浏览。
- 由于分离,人们能够更容易地find并重用SQL代码。
- 在模式更改时更容易更改 – 只需将相同的输出提供给代码,就可以正常工作
- 更容易移植到不同的数据库。
- 我可以列出我的存储过程的个人权限,并控制该级别的访问权限。
- 我可以configuration我的数据查询/持久性代码与数据转换代码分开。
- 我可以在我的存储过程中实现可更改的条件,并且可以很容易地在客户站点进行自定义。
- 使用一些自动化工具将我的模式和语句转换到一起变得更加容易,而不是当我将代码embedded到代码中时,我将不得不将它们search到。
- 当您将所有的数据访问代码放在单个文件中时,确保数据访问的最佳实践更为简单 – 我可以检查访问非高性能表的查询或使用更高级别序列化的查询,或者在代码中select* 。
- 当所有文件都列在一个文件中时,更容易find模式更改/数据操作逻辑更改。
- 当SQL处于同一位置时,执行search和replaceSQL编辑变得更容易,例如,更改/添加所有存储过程的事务隔离语句。
- 我和DBA的人发现,有一个单独的SQL文件更容易/方便,当DBA必须审查我的SQL的东西。
- 最后,您不必担心SQL注入攻击,因为您的团队的一些懒惰成员在使用embedded式sqls时未使用参数化查询。
存储过程的性能优势往往是可以忽略的。
存储过程的更多优点:
- 防止逆向工程(如果使用encryption创build,当然)
- 更好地集中数据库访问
- 能够透明地更改数据模型(无需部署新客户端); 如果多个程序访问相同的数据模型特别方便
我在代码方面。 我们构build了所有应用程序(包括Web和客户端)所使用的数据访问层,因此从这个angular度来看它是干的。 它简化了数据库部署,因为我们只需要确保表格模式是正确的。 它简化了代码维护,因为我们不必查看源代码和数据库。
我没有太多的问题与数据模型的紧密耦合,因为我不知道在哪里可以真正打破这种耦合。 应用程序及其数据本质上是耦合的。
存储过程。
如果错误消失或逻辑改变了一点,则不必重新编译项目。 另外,它允许来自不同来源的访问,而不仅仅是您在项目中编码查询的地方。
我不认为维护存储过程比较困难,你不应该直接将它们编码到数据库中,而是先在单独的文件中编码,然后你可以在任何需要设置的数据库上运行它们。
存储过程的优点 :
更容易的代码审查。
较less耦合,因此更容易testing。
更容易调整。
从networkingstream量的angular度来看,性能通常更好 – 如果你有一个游标或者类似的东西,那么就不会有多次访问数据库
您可以更轻松地保护对数据的访问,删除对表的直接访问,通过procs强制执行安全性 – 这也使您能够相对快速地find更新表的任何代码。
如果还有其他服务(比如报告服务),你可能会发现将所有的逻辑存储在一个存储过程而不是代码中,并且不得不复制它
缺点:
开发人员更难pipe理:脚本的版本控制:每个人都有自己的数据库,是与数据库和IDE集成的版本控制系统吗?
在某些情况下,在代码中dynamic创build的sql可能比存储过程具有更好的性能。 如果你已经创build了一个存储过程(比如说sp_customersearch),这个存储过程非常复杂并且有很多参数,因为它必须非常灵活,你可以在运行时在代码中生成一个简单得多的sql语句。
有人可能会争辩说,这只是将一些处理从SQL移动到Web服务器,但总的来说,这将是一件好事。
关于这种技术的另一个好处是,如果你正在查看SQL分析器,你可以看到你生成的查询和debugging,比看到一个20个参数的存储过程调用要容易得多。
我喜欢存储过程,不知道有多less次能够使用存储过程对应用程序进行更改,而不会对应用程序产生任何停机时间。
Transact SQL的粉丝,调整大型查询已被certificate对我非常有用。 大约6年没有写入任何内联SQL!
您列出了sprocs的2个积分:
性能 – 不是真的。 在Sql 2000或更高的查询计划优化是相当不错的,并caching。 我敢肯定,甲骨文等做类似的事情。 我不认为有一个性能sprocs的情况下。
安全? 为什么sprocs会更安全? 除非你有一个漂亮的不安全的数据库,否则所有的访问都将从你的数据库pipe理员或通过你的应用程序。 总是参数化所有的查询 – 从来没有从用户input内联,你会没事的。
无论如何,这是性能的最佳实践。
Linq绝对是我现在开始一个新项目的方式。 看到这个类似的post 。
@Keith
安全? 为什么sprocs会更安全?
正如Komradekatz所build议的,您可以禁止访问表(连接到数据库的用户名/密码组合),并且只允许SP访问。 这样,如果有人获得用户名和密码到您的数据库,他们可以执行SP的,但不能访问表或数据库的任何其他部分。
(当然,执行sprocs可能会给他们所需要的所有数据,但这取决于可用的sprocs。让他们访问表让他们能够访问所有的东西。)
想想这样
你有4个Web服务器和一堆使用相同的SQL代码的Windows应用程序现在你意识到有一个SQl代码的小问题,所以你宁愿……改变一个地方的proc或推动代码到所有网页服务器,重新安装所有的桌面应用程序(clickonce可能会帮助)所有的窗口框
我更喜欢存储特效
对一个进程执行性能testing也比较容易,把它放在查询分析器中设置统计信息io /时间设置上showplan_text on和瞧
不需要运行profiler来查看被调用的内容
只是我2美分
我更喜欢用代码保存(使用ORM,而不是内联或临时),所以它们被源代码控制覆盖,而不必处理保存.sql文件。
另外,存储过程本质上并不安全。 你可以像使用内联一样轻松地使用sproc写一个错误的查询。 参数化的内联查询可以像存储过程一样安全。
使用你的应用程序代码作为最好的:处理逻辑。
用你的数据库做最好的事情:存储数据。
您可以debugging存储过程,但会发现更容易debugging和维护代码中的逻辑。 每次更改数据库模型时,通常都会结束重新编译代码。
此外,带有可选search参数的存储过程是非常不方便的,因为您必须提前指定所有可能的参数,并且复杂的search有时是不可能的,因为您无法预测参数将要重复多less次。
说到安全性,存储过程要安全得多。 有人认为,无论如何,所有访问都将通过应用程序。 很多人遗忘的事情是大多数安全漏洞来自公司内部。 想想有多less开发人员知道你的应用程序的“隐藏”用户名和密码?
此外,正如MatthieuF指出的那样,由于应用程序(无论是在台式机还是Web服务器上)和数据库服务器之间的往返次数较less,性能可以大大提高。
根据我的经验,通过存储过程抽象数据模型也大大提高了可维护性。 作为过去需要维护多个数据库的人,在面对所需的模型更改时能够简单地改变一两个存储过程,并且使所有外部应用程序完全透明地进行更改是一种解脱。 很多时候,您的应用程序并不是唯一指向数据库的应用程序 – 还有其他应用程序,报告解决scheme等,因此,跟踪所有受影响的点可能会带来开放式访问表的麻烦。
我还会在加号列中join检查,以便将SQL编程交给专业人员,而SP则使查找和testing/优化代码更容易。
我看到的一个缺点是许多语言不允许传递表参数,所以传递未知数字的数据值可能会令人讨厌,而且有些语言仍然无法处理从单个存储过程中检索多个结果集(尽pipe后者在这方面不会使SP比内联SQL糟糕)。
其中一个来自Microsoft TechEd会议的关于我参与的安全性的build议,是通过存储的特效进行所有调用,并拒绝直接访问表。 这种方法被认为是提供额外的安全性。 我不确定这是否值得它只是为了安全,但如果你已经使用存储的特效,它不会伤害。
如果你把它放在一个存储过程中,保存起来更容易。 如果在将来有可能发生变化的逻辑涉及到困难,那么当您有多个客户端连接时,将其放入数据库当然是一个好主意。 例如,我现在正在开发一个应用程序,它有一个最终用户的Web界面和一个pipe理桌面应用程序,两者都共享一个数据库(显然),我试图尽可能多地保留数据库的逻辑。 这是DRY原则的一个很好的例子。
我坚持存储过程的一面假设你不作弊,并使用存储过程中的dynamicSQL。 首先,使用存储过程允许dba在存储的proc级别而不是表级别设置权限。 这不仅对于打击SQL注入攻击是至关重要的,而且也是防止内部人员直接访问数据库和改变事物的关键。 这是一种有助于防止欺诈的方法。 没有包含个人信息(SSN,信用卡号码等)的数据库,或者无论如何创build金融交易都应该被访问,除非通过程序化的程序。 如果您使用任何其他方法,则您将公司的个人数据库全部开放,以创build假金融交易或窃取可用于身份盗用的数据。
存储过程也比从应用程序发送的SQL更容易维护和性能优化。 他们还允许dba了解数据库结构变化对数据访问方式的影响。 我从来没有遇到一个好的dba谁会允许dynamic访问数据库。
我们在现在使用的Oracle数据库中使用存储过程。 我们也使用Subversion。 所有的存储过程创build为.pkb&.pks文件并保存在Subversion中。 我之前完成了在线SQL,这是一个痛苦! 我更喜欢我们在这里做的方式。 创build和testing新的存储过程比在代码中执行要容易得多。
有一个
较小的日志
存储过程的另一个未知的专业人员尚未提到:当谈到SQLstream量,基于SP的数据访问产生更less的stream量。 当您监控stream量进行分析和分析时,这一点变得非常重要 – 日志会小得多,可读性强。
我不是存储过程的忠实粉丝,但我在一个条件下使用它们:
当查询非常庞大时,最好将它作为存储过程存储在数据库中,而不是从代码中发送。 这样,不用从应用程序服务器向数据库发送大量的string,只会发送"EXEC SPNAME"
命令。
当数据库服务器和Web服务器不在同一个networking上时(例如,互联网通信),这是过度的。 即使情况并非如此,太多的压力意味着很多浪费的带宽。
但是,男人,他们是如此可怕的pipe理。 我尽可能避免他们。
SQL存储过程不会增加查询的性能
显然,使用存储过程比在代码中构buildSQL有几个优点。
- 您的代码实现和SQL彼此独立。
- 代码更容易阅读。
- 写一次使用多次。
- 修改一次
- 不需要给程序员提供关于数据库的内部细节。 等等
存储过程更易于维护,因为:
- 只要你想改变一些SQL,你不必重新编译你的C#应用程序
- 你最终重用SQL代码。
代码重复是您尝试构build可维护的应用程序时可以做的最糟糕的事情!
当发现需要在多个地方纠正的逻辑错误时会发生什么? 你更容易忘记改变你复制和粘贴你的代码的最后一个位置。
在我看来,性能和安全性的收益是一个附加。 您仍然可以编写不安全/低效的SQL存储过程。
更容易移植到另一个数据库 – 没有过程端口
将所有存储过程编写成另一个数据库的脚本并不难。 事实上 – 这比导出表容易,因为没有主键/外键需要担心。
@Traprap – sprocs同样容易受到注入攻击。 就像我说的:
总是参数化所有的查询 – 从来没有从用户input内联,你会没事的。
这适用于sprocs和dynamicSql。
我不确定不重新编译你的应用程序是一个优势。 我的意思是,无论如何你都要在运行之前对你的代码(应用程序和数据库)进行unit testing。
@Guy – 是的,sprocs让你控制应用程序用户,使他们只能执行sproc,而不是基本的动作。
我的问题是:如果所有通过你的应用程序访问它,使用连接和有限权限的用户更新/插入等,这额外的水平增加安全性或额外的pipe理?
我的意见是非常后者。 如果他们已经妥协了应用程序,可以重新编写它们,那么他们还有很多其他的攻击可以使用。
如果dynamic内联代码,仍然可以针对这些sprocs执行Sql注入,所以黄金法则仍然适用,所有用户input必须始终进行参数化。
到目前为止我还没有见过的东西:那些最了解数据库的人并不总是写应用程序代码的人。 存储过程给数据库人员提供了一种与程序员进行交互的方式,他们并不真正想学习那么多关于SQL的知识。 大数据库尤其是遗留数据库并不是最容易理解的最简单的事情,所以程序员可能更喜欢一个简单的接口,让他们知道他们需要什么:让数据库pipe理员找出如何join17个表来实现这一目标。
这就是说,用来编写存储过程的语言(PL / SQL是一个臭名昭着的例子)是非常残酷的。 他们通常不会提供您在当今stream行的命令,OOP或function语言中看到的任何细节。 认为COBOL。
所以,坚持存储过程只是抽象出关系细节,而不是那些包含业务逻辑的细节。
我通常写OO代码。 我怀疑你们中的大多数人也可能会这样做。 在这种情况下,似乎很明显,所有的业务逻辑(包括SQL查询)都属于类定义。 将逻辑部分分解成部分驻留在对象模型中,部分驻留在数据库中并不比将业务逻辑放入用户界面更好。
早些时候有关存储过程的安全性好处的答复已经说了很多。 这些分为两大类:
1)限制直接访问数据。 这在某些情况下非常重要,当你遇到一个时,存储过程几乎是你唯一的select。 根据我的经验,这种情况是例外而不是规则。
2)SQL注入/参数化查询。 这个反对意见是一个红鲱鱼。 内联SQL – 即使是dynamic生成的内联SQL – 也可以像任何存储过程一样完全参数化,在任何现代语言中都可以轻松完成。 这里没有任何优势。 (“懒惰的开发人员可能不打扰使用参数”并不是一个有效的反对意见,如果你的团队中的开发人员更喜欢将用户数据连接到他们的SQL而不是使用参数,那么你首先试着去教育他们,如果这样做不起作用,就像开发者有其他不好的,明显有害的习惯一样)。
我是代码超过SPROC的一个巨大的支持者。 首要的原因是保持代码紧密耦合,紧接着的是源代码控制的简便性,而没有大量的定制实用程序来引入它。
在我们的DAL中,如果我们有非常复杂的SQL语句,我们通常会将它们包含为资源文件并根据需要进行更新(也可能是一个单独的程序集,并且每db分配一次,等等)。
这使我们的代码和我们的sql调用保存在相同的版本控制中,而不会“忘记”运行某些外部应用程序进行更新。