原文出处:MSDN:An Evaluation of Stored Procedures for the .NET Developer
日期:2004年3月
本文适用于:
Microsoft® SQL Server™
Microsoft Visual Studio® .NET 2003
Transact-SQL (T-SQL) Language
摘要
向那些在工作中主要使用.net编程语言的开发者简要介绍微软SQL Server数据库中的存储过程。揭示使用存储过程的利弊,并简要介绍在Visual Studio.net 2003中提供的一些工具和一些较好的练习,它们可以帮助你轻松的开始。
序言
为何考虑存储过程?
性能
可维护性与共性提取
安全性
存储过程适合我么?
Visual Studio .NET提供的工具
查看存储过程
创建和修改存储过程
入门技巧
使用SET NOCOUNT ON 语句
尽量少使用可选参数
需要时使用输出参数
提供一个返回值
先写DDL,再写DML
永远要写注释
总结
序言
本文的主要目的是根据应用程序的需要,介绍一些将T-SQL语句封装入存储过程时的关键性要素,以便当你在自己的系统中应用他们时可以做出明智的决定。我将介绍一些工具和较好的练习给那些希望在.net应用程序中利用它们的开发人员。
我很清楚,我现在介入了一场类似不同宗教之间的斗争,一场关于事务逻辑应该仅仅建立在中间层还是仅仅建立在数据层的斗争;或者说是仅仅在程序中编写查询代码还是全部采用存储过程的争论。当然,这两种方法各有利弊。重要的是我们要考虑什么对我们的程序和系统环境比较重要。所以,让我们来看看存储过程到底是什么,并考虑我们用它来封装T-SQL语句的原因是什么。
为何考虑存储过程?
也许你经常在程序中利用SqlCommand对象和编写T-SQL语句来完成数据操作,但从没有考虑过寻找更好的地方来存放这些T-SQL语句,而不是将他们与你的数据访问代码混合在一起。也许随着时间的流逝,你的应用程序的功能不断增加,造成了其中存在很多复杂的T-SQL语句代码。存储过程是一种封装这些复杂语句的更好的方法。
也许大多数人都对存储过程有所了解,但对于那些不了解它的人,存储过程就是一组许多预先编写好的T-SQL语句序列,并作为一个独立的代码单元保存在数据库中。你可以通过输入参数给它传送运行时信息,也可以通过结果集或输出参数获取返回数据。当存储过程第一次运行时,它就被编译了。它产生了一个执行序列,实质上是一条记录,记录了在存储过程中指定的、用以获得结果的T-SQL语句的执行步骤。之后,这个执行序列便被保存在缓存中以备后用。这将提高存储过程的性能,因为当再一次执行时,存储过程不需要分析代码去执行任务,而是简单地转向保存在缓存中的执行序列。缓存中的内容在SQL Server重新启动之前和在它的内存生命期内都一直有效,它的内存生命期取决于它的最低内存消耗。
性能
在查询时,这些缓存中的执行序列可以提高存储过程的性能。然而,在最近的两个SQL Server版本中,所有的T-SQL语句批处理都被以执行序列的形式保存在缓存中,而不管它是否存在于存储过程中。因此,在基于此特性上的性能的提高不再是存储过程的卖点。任何采用静态(不经常改变)语法的T-SQL批处理被频繁地提交,以防止它在缓存中的执行序列因为超过内存生命期而消失,这样做同样可以得到很高的性能。注意这里“静态”很 关键,因为即使无关紧要的变化(例如注释的改变)都会导致不与缓存中的执行序列进行匹配。从而也就导致了执行序列的无法重用。
可是,存储过程仍旧有它的优点,利用它可以减少网络的数据传输量。你只需要发送如下的指令:
EXECUTE stored_proc_name便可以轻松地执行一系列复杂的操作,而不是那些传统的冗长的T-SQL语句。一个设计良好的存储过程可以显著减少客户端与服务器端的往返通讯,甚至可以压缩至一次调用。
可维护性与共性提取
第二个值得考虑的潜在好处是可维护性。在理想的情况下你的数据库架构不会改变,你的事务规则也不会改变;但在现实中这些是经常发生的。例如:当需要为新的促销活动添加x,y,z表时,你可以通过修改存储过程简单地将这些数据包含近来,而不需要再到你的程序代码中到处寻找需要修改的语句。在存储过程中修改代码对于应用程序来说是透明的,你依旧可以得到相同的销售信息,虽然存储过程的内部实现已经改变了。相对于你修改现有代码,重新测试和配置程序来说,直接修改存储过程更加省时省力。
而且,通过提取一些相同的实现方法并将其保存在存储过程中,当应用程序需要通过这些方法访问数据时只需采用相同的操作。你便不用维护散布在许多地方的相同代码了,并保证你的用户可以获得一致的信息。
另外一个对维护有好处的是,将T-SQL语句保存在存储过程中也是一种较好的控制版本的方法。你可以利用那些创建和修改存储过程的脚本来做版本控制,就像控制源代码模块的版本一样。通过使用微软提供的Visual SourceSafe和其它一些版本控制工具,你可以很容易地引用和恢复到旧的版本。
请不要误解,使用存储过程来提高可维护性并不能避免需要修改数据架构和事务规则的可能。当这些改变过大时,就需要更改存储过程的输入参数与返回值,而且要修改你的前台程序代码例如添加参数、更新GetValue()函数的调用等等。
另外需要考虑的问题是,使用存储过程封装事务逻辑限制了你应用程序的可移植性,那样便被束缚在SQL Server上了。如果可移植性对于你的系统环境来说制关重要的话,那么将事务逻辑封装在与关系数据库无关的中间层中是个不错的选择。
安全性
使用存储过程的最后一个原因就是它可以提高系统的安全性。
在规范用户访问信息方面,它可以提供给用户经过授权的指定信息而不是那些非授权的表的内容。除了存储过程可以接受用户输入而动态改变数据显示的功能外,你可以将他想象为SQL Server中的视图(如果你对视图比较熟悉的话)。
它还可以帮助你逃离代码的安全隐患。保护你免受一种叫做‘SQL注射’的攻击,这种攻击主要会在你的合法输入参数中添加如AND 或者 OR的操作符。存储过程还会隐藏事务逻辑的实现以减少你的应用程序泄密的危险。因为事务逻辑非常重要,这种信息被认为是知识产权。
除此之外,使用存储过程时,你可以利用ADO.net提供的SqlParameter类,为存储过程提供数据类型正确的参数。这也为我们提供了一种用来验证用户输入参数的方法,同时也是深度防范策略的一部分。注意,在单独的查询语句中,参数同样可以起到缩小用户输入范围的作用。
但是也请注意,使用存储过程来提高安全性时,如果不作好安全设置以及没有良好的编码习惯,仍旧会使你暴露在进攻之下。在创建SQL Server角色和分配权限时,如果不注意就会导致用户访问到他们不该看到的数据。不要绝对的认为使用存储过程可以彻底避免''SQL注射''攻击,在输入参数后面追加一些数据操纵语言(DML)一样可以进行攻击。
因此,使用参数来验证输入数据的类型也不是十分稳妥,无论是在一条T-SQL语句中还是在存储过程中,所有用户输入的数据,尤其是那些文本数据,应该在传送到数据库之前采用额外的验证。
存储过程适合我么?
哦,也许是的。让我们回顾一下它的优点:
create procedure test_MyStoredProc @param1 int不要使用SP_前缀 SP_前缀是为系统级的存储过程所保留的。数据库引擎总是首先在master数据库中寻找有SP_前缀的存储过程。这就意味着首先会在master数据库中搜寻一遍,之后才会到达需要执行的存储过程那里,这就会花掉一些时间。更糟的是,如果在系统级的存储过程中存在与你的存储过程同名的,那你的存储过程将永远不会被执行。
as
set nocount on