存储过程一般用于处理比较复杂的任务,基础ms这个平台,可以大大降低耗时,其编译机制也提高了数据库执行速度。
当然在系统控制方便方面,例如当系统进行调整时,这是只需要将后台存储过程进行更改,而不需要更改客户端程序。也无需重新安装客户端应用程序。
存储过程不仅仅适用于大型项目,对于中小型项目,使用存储过程也是非常有必要的。其威力和优势主要体现在:
1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。(这涉及到原理性的问题,你记住就好!)
2.当对数据库进行复杂操作时(如对多个表进行 Update,Insert,Query,Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。这些操作,如果用程序来完成,就变成了一条条的 SQL 语句,可能要多次连接数据库。而换成存储,只需要连接一次数据库就可以了。
(尽可能少的连接数据库,可以减少时间损耗;事务方面在批量操作中非常重要,因为事务可以回溯,当出错时,可以进行回溯,保证数据的完整性!)
3.存储过程可以重复使用,可减少数据库开发人员的工作量。
(体现在分页存储过程,以及下面这个例子:)
例子:create PROC [dbo].[jobs_public_select]
@TableName VARCHAR(2000),/*表名*/
@ParamName VARCHAR(2000),/*查询字段字符串*/
@ParamWhere NVARCHAR(2000)/*条件字符串*/
AS
BEGIN
Declare @SQL varchar(500)
set @SQL='SELECT '+@ParamName+' from '+@TableName+' WHERE 1=1'
IF @ParamWhere<>''
BEGIN
SET @SQL=@SQL+@ParamWhere
END
exec(@SQL)
END
(这个例子主要作用就是公共查询功能,你只需要传递表名,查询的字段,条件即可。你可以以此类推,写个公共删除,公共更新的;操作无非也这几种哈。)
4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权。
(这方面在赋权限,主要体现在,连接时采用哪个用户连接数据库,而对应的这个用户也有对应的数据库操作权限。)
优点:
1.速度快。尤其对于较为复杂的逻辑,减少了网络流量之间的消耗
我有的过程和函数达到了几百行,一个微型编译器,相信用程序就更麻烦了。
(在获取权限那个存储过程深有体会,你也可以写个C#的算法,然后与存储过程进行速度比较。)
2.写程序简单,采用存储过程调用类,调用任何存储过程都只要1-2行代码。
(我不知道别人怎么调用,我是深受其益)
3.升级、维护方便(你只需要更改存储过程就好,比如添加一个字段等)
4.调试其实也并不麻烦,可以用查询分析器(基础好,一般没有遇到很大的错误!)
5.如果把所有的数据逻辑都放在存储过程中,那么asp.net只需要负责界面的显示阿什么的,出错的可能性最大就是在存储过程。我碰到的就一般是这种情况。
(减少了排错的时间)
缺点:
1.可移植性差,我一直采用sql server开发,可是如果想卖自己的东西,发现自己简直就是在帮ms卖东西,呵呵。想换成mysql,确实移植麻烦。
2.采用存储过程调用类,需要进行两次调用操作,一次是从sql server中取到过程的参数信息,并且建立参数;第二次才是调用这个过程。多了一次消耗。
不过这个缺点可以在项目开发完成,过程参数完全确定之后,把所有过程参数信息倒入到一个xml文件中来提高性能。
当一个业务同时对多个表进行处理的时候采用存储过程比较合适。
1. 使用存储过程在一般情况下会提高性能,因为数据库优化了存储过程的数据访问计划并应用缓存方便以后的查询;
2. 存储过程单独保护存在于数据库中。客户端可以获取权限执行存储过程,而不需要对底层的具体表设置其他的访问权限;
3. 存储过程会使得维护起来更加方便,因为通常修改一个存储过程要比在一个已经发布的组件中修改SQL语句更加方便;
4. 存储过程给底层数据格式增添了额外的抽象层。使得使用存储过程的客户端对存储过程的实现细节以及对底层数据格式是隔离独立的;
5. 存储过程能够缓解网络带宽,因为可以批量执行SQL语句而不是从客户端发送超负载的请求。
复杂的数据处理用存储过程,如有些报表处理
多条件多表联合查询,并做分页处理
总结:
1. 当一个事务涉及到多个SQL语句时或者涉及到对多个表的操作时就要考虑用存储过程;
2. 当在一个事务的完成需要很复杂的商业逻辑时(比如,对多个数据的操作,对多个状态的判断更改等)要考虑;
3. 还有就是比较复杂的统计和汇总也要考虑,但是过多的使用存储过程会降低系统的移植性。
分页例子:
create procedure [dbo].[sp_super_page]
@TableName varchar(5000), --要进行分页的表,也可以用联接,如dbo.employee或dbo.employee INNER JOIN dbo.jobs ON (dbo.employee.job_id=dbo.jobs.job_id)
@Fields varchar(5000), --表中的字段,可以使用*代替
@OrderField varchar(5000), --要排序的字段
@sqlWhere varchar(5000), --WHERE子句
@pageSize int, --分页的大小
@pageIndex int, --要显示的页的索引
@TotalPage int output, --页的总数
@TotalRecords int output--信息总条数
as
begin
Begin Tran
Declare @sql nvarchar(4000);
Declare @totalRecord int; --记录总数
if (@sqlWhere IS NULL or @sqlWhere = '')
--在没有WHERE子句的情况下得到表中所有的记录总数
set @sql = 'select @totalRecord = count(*) from ' + @TableName
else
--利用WHERE子句进行过滤
set @sql = 'select @totalRecord = count(*) from ' + @TableName+ ' where ' + @sqlWhere
--执行sql语句得到记录总数
EXEC sp_executesql @sql,N'@totalRecord int OUTPUT',@totalRecordOUTPUT
select @TotalPage=CEILING((@totalRecord+0.0)/@PageSize)
--根据特定的排序字段为为行分配唯一ROW_NUMBER的顺序
if (@sqlWhere IS NULL or @sqlWhere = '')
set @sql = 'select * from (select ROW_NUMBER() over(order by '+ @OrderField + ') as rowId,' + @Fields + ' from ' + @TableName
else
set @sql = 'select * from (select ROW_NUMBER() over(order by '+ @OrderField + ') as rowId,' + @Fields + ' from ' + @TableName + ' where ' + @SqlWhere
--确保当前页的索引在合理的范围之内
if @PageIndex<=0
Set @pageIndex = 1
if @pageIndex>@TotalPage
Set @pageIndex = @TotalPage
--得到当前页在整个结果集中准确的ROW_NUMBER值
Declare @StartRecord int
Declare @EndRecord int
set @StartRecord = (@pageIndex-1)*@PageSize + 1
set @EndRecord = @StartRecord + @pageSize - 1
--输出当前页中的数据
set @Sql = @Sql + ') as t' + ' where rowId between ' +Convert(varchar(50),@StartRecord) + ' and ' + Convert(varchar(50),@EndRecord)
Exec(@Sql)
If @@Error <> 0
Begin
RollBack Tran
SET @TotalRecords=-1
End
Else
Begin
Commit Tran
SET @TotalRecords=@totalRecord
End
end