在许多关系型数据库教材中,都推荐使用存储过程,并列举种种好处。比如以下来自微软官方文档的解释:
使用存储过程的好处
下表介绍了使用过程的一些好处。 减少了服务器/客户端网络流量 过程中的命令作为代码的单个批处理执行。 这可以显著减少服务器和客户端之间的网络流量,因为只有对执行过程的调用才会跨网络发送。 如果没有过程提供的代码封装,每个单独的代码行都不得不跨网络发送。 更强的安全性 多个用户和客户端程序可以通过过程对基础数据库对象执行操作,即使用户和程序对这些基础对象没有直接权限。 过程控制执行哪些进程和活动,并且保护基础数据库对象。 这消除在了单独的对象级别授予权限的要求,并且简化了安全层。 可在 CREATE PROCEDURE 语句中指定 EXECUTE AS 子句以便实现对其他用户的模拟,或者使用户或应用程序无需针对基础对象和命令的直接权限,即可执行某些数据库活动。 例如,某些操作(如 TRUNCATE TABLE)没有可授予的权限。 若要执行 TRUNCATE TABLE,用户必须对指定表具有 ALTER 权限。 授予用户对表的 ALTER 权限可能不是最佳方法,因为用户将拥有超出截断表的能力的权限。 通过将 TRUNCATE TABLE 语句纳入模块中并指定该模块作为一个有权修改表的用户执行,您可以将截断表的权限扩展至授予其对模块的 EXECUTE 权限的用户。 在通过网络调用过程时,只有对执行过程的调用是可见的。 因此,恶意用户无法看到表和数据库对象名称、嵌入自己的 Transact-SQL 语句或搜索关键数据。 使用过程参数有助于避免 SQL 注入攻击。 因为参数输入被视作文字值而非可执行代码,所以,攻击者将命令插入过程内的 Transact-SQL 语句并损害安全性将更为困难。 可以对过程进行加密,这有助于对源代码进行模糊处理。 有关详细信息,请参阅 SQL Server Encryption。 代码的重复使用 任何重复的数据库操作的代码都非常适合于在过程中进行封装。 这消除了不必要地重复编写相同的代码、降低了代码不一致性,并且允许拥有所需权限的任何用户或应用程序访问和执行代码。 更容易维护 在客户端应用程序调用过程并且将数据库操作保持在数据层中时,对于基础数据库中的任何更改,只有过程是必须更新的。 应用程序层保持独立,并且不必知道对数据库布局、关系或进程的任何更改的情况。 改进的性能 默认情况下,在首次执行过程时将编译过程,并且创建一个执行计划,供以后的执行重复使用。 因为查询处理器不必创建新计划,所以,它通常用更少的时间来处理过程。 如果过程引用的表或数据有显著变化,则预编译的计划可能实际上会导致过程的执行速度减慢。 在此情况下,重新编译过程和强制新的执行计划可提高性能。
|
个人猜测这个观点是从传统OLTP系统性能优化的角度考虑的。由于OLTP的操作往往需要短平快,传输的数据量不大。比如需要返回的一行记录可能只有几十个字节,不用存储过程的话,为了获取这几十个字节却需要向服务器发送1KB甚至更长的SQL语句,性价比实在太不理想。当然也有代码逻辑重用,执行计划重用等等其它方面的考虑,在此先不讨论。
但是在OLAP系统中,一条SQL语句往往会返回大量数据,并运行较长时间。这种情况下,是否使用存储过程对性能的影响就显得不那么明显。而且要注意的是,实际项目开发决策需要参考很多方面,性能只是各种因素其中之一。使用存储过程的话,开发过程中经常碰到以下比较头痛的问题:
- 搞不清调用这个存储过程的是哪个SSIS包,哪个SSRS报表,哪个SSAS数据视图,哪个程序块。一旦存储过程需要更改,很难快速查清受影响的范围。有的项目有自己的方法来解决这个问题,比如采用特定的命名规则之类的,然而非常高效通用的办法还是比较少见。
- 虽然将数据库代码加入源代码管理是比较推荐的做法,但仍然有许多项目由于种种客观条件的限制难以做到这一点(抱歉,这样说有点笼统,请自行体会)。在这样的情况下,存储过程代码往往失于管理,不知道谁在什么时候修改了哪些存储过程的哪些地方。这些问题的一个典型特征是在编码行为上间接对项目组成员产生难以察觉的影响,例如有些人会形成“将旧代码注释掉并保留”的习惯以便于未来察看代码的旧貌,有些人会在修改存储过程之前备份数据库,有些人则喜欢在修改存储过程前创建一个副本。。。这些行为的根本问题,正是由于没有对存储过程进行源代码管理。
- 许多小型项目缺乏严谨的权限管理,不能删除不能修改的存储过程常常被意外地删除或者修改。
特别是对于长期的开发项目,代码管理的重要性往往大于对性能的需求。因此我认为,针对上述问题,在OLAP环境中,一方面应该坚持将数据库代码加入源代码管理,另一方面应该将SQL语句直接写在调用方代码里以便追踪代码变更的影响。
欢迎加入SQL Server精英群参与技术交流