存储过程就是把一个或多个T-SQL语句组合到一个逻辑单元中,在SQL Server数据库中保存为一个对象。在创建完成后,定义会保存在sys.sql_modules系统目录视图中。
存储过程的有点:
1、存储过程是在数据层汇集的T-SQL代码,可以促进代码的重复使用,同时有助于调试、寻找bug。也就是说这些代码就存放在数据库里,而不是嵌入在应用程序中的SQL代码,再由这些代码连接起来,最后发送到服务器上来执行的。
2、存储过程相对于大的即席查询,能够减少网络流量。
3、存储过程则更安全,因为内嵌的即席查询容易进行SQL注入。
4、相对于函数、视图,存储过程能使用流控制、临时表、表变量等。
5、可以淡化数据获取的方法。一旦底层的表有变化,不用去修改嵌入到应用程序中的sql代码,只要修改存储过程就可以了。
6、存储过程的查询响应时间比较稳定。存储过程提供了更加可靠的查询计划缓存,因此可以重用,而即席查询完全依赖于执行计划被缓存的环境(语句中的参数)。
另外,需要注意的:'CREATE/ALTER PROCEDURE'不允许将数据库名称指定为对象名前缀
--1.1创建无参数存储过程
--这个存储过程的定义很像视图定义
create procedure dbo.usp_wct
as
select wcId,
wcV,
wcDate
from wcT
go
--1.2执行存储过程
exec dbo.usp_wct
--2.1创建带参数的存储过程
create procedure dbo.usp_param_wct
(@wcId bigint =1, --默认值为1
@wcDate datetime =null) --默认值为null
as
select wcV
from dbo.wcT
where wcId = @wcId
and isnull(@wcDate,1)=case when @wcdate is null
then 1
else wcDate
end
--返回状态值,一般不用于返回计算结果,而是用来返回表明存储过程执行的状态
return 1
go
--2.2.1调用存储过程
exec dbo.usp_param_wct '1','2012-07-01'
--2.2.2调用存储过程,带参数名
exec dbo.usp_param_wct @wcid='1',
@wcdate='2012-07-01'
--2.2.3调用存储过程,带参数名,只输入一个参数值,另一个会使用默认值
exec dbo.usp_param_wct @wcid='1'
--2.2.4调用存储过程,带参数名,不输入参数,2个参数都会使用默认值
exec dbo.usp_param_wct
--3.1创建带output参数的存储过程
create procedure dbo.usp_output_param_wct
(@wcId bigint =1, --默认值为1
@wcDate datetime =null, --默认值为null
@wcV varchar(50) output) --输出参数,用来返回计算结果
as
select @wcV = wcV
from dbo.wcT
where wcId = @wcId
and isnull(@wcDate,1)=case when @wcdate is null
then 1
else wcDate
end
--返回状态值,一般不用于返回计算结果,而是用来返回表明存储过程执行的状态
return 1
go
--3.2调用存储过程
declare @output varchar(50)
exec dbo.usp_output_param_wct @wcId =1, --输入参数
--@wcDate = null,
@wcv = @output output --输出参数,存放返回值
print @output
--4.1修改存储过程
create procedure dbo.usp_output_param_wct
(@wcId bigint =1, --默认值为1
@wcDate datetime =null, --默认值为null
@wcV varchar(50) output) --输出参数,用来返回计算结果
with encryption --加密存储过程
as
select @wcV = wcV
from dbo.wcT
where wcId = @wcId
and isnull(@wcDate,1)=case when @wcdate is null
then 1
else wcDate
end
--返回状态值,一般不用于返回计算结果,而是用来返回表明存储过程执行的状态
return 1
go
--4.2查看定义
select object_name(object_id),
definition --显示为NULL
from sys.sql_modules
where object_id = object_id('dbo.usp_output_param_wct')
--5.在SQL Server启动时自动执行存储过程
create procedure dbo.create_table
as
select * into wc.dbo.wcX
from wc.dbo.wcT
go
--5.1设置为true,在SQL Server启动时自动执行存储过程
exec sp_procoption @procname = 'dbo.create_table',
@optionname = 'startup',
@optionvalue= 'true'
--5.2设置为false来禁止
--只有dbo拥有的master数据库中的对象才能更改启动设置。
use master
go
exec sp_procoption @procname = 'dbo.create_table',
@optionname = 'startup',
@optionvalue= 'false'
--6.1每次存储过程执行时都重新编译
--需要注意的是:当表、索引数据大量更新时,会进行语句级别的重新编译
create procedure dbo.usp_backupset_info
(@startDate datetime,
@finishDate datetime)
with recompile --不保存执行计划,每次都是重新编译
as
select SUM(backup_size)/1024/1024/1024 as 'GB'
from msdb.dbo.backupset
where backup_start_date >= @startDate
and backup_finish_date <= @finishDate
and type = 'D' --数据库备份,不是日志备份或差异备份
go
--6.2清空过程缓存
select *
from sys.dm_exec_cached_plans
dbcc freeproccache --清空过程缓存
--再次查询发现都已经清空
select *
from sys.dm_exec_cached_plans
--7.刷新存储过程,查看元数据
exec sp_refreshsqlmodule @name ='dbo.usp_output_param_wct'
select p.name, --存储过程名称
p.object_id,
pp.parameter_id,
pp.name, --参数名称
pp.default_value, --好像不起作用
t.name --参数列的类型
from sys.procedures p
inner join sys.parameters pp
on pp.object_id = p.object_id
inner join sys.types t
on t.system_type_id = pp.system_type_id
where p.object_id =965578478
--8.删除存储过程
DROP PROCEDURE dbo.usp_output_param_wct
存储过程的Execute as选项
--1.创建表
create table dbo.wcE
(vid int not null primary key clustered,
v int not null
)
insert into dbo.wcE
values(1,100),
(2,200),
(3,300)
--2.创建存储过程,存储过程的架构与存储过程中引用对象的架构一样
create procedure dbo.usp_delete_wcE
as
delete from dbo.wcE
go
--3.1创建登录名
create login wclogin with password = 'wclogin123'
--3.2创建数据库用户
create user wclogin
--4.授予用户执行存储过程的权限
grant exec on dbo.usp_delete_wcE to wclogin
grant connect to wclogin
/*====================================================
5.1打开另一个客户端,以wclogin登录名登进去,然后执行存储过程,
虽然这个用户没有访问存储过程中所引用表的权限,
但是由于存储过程的架构与存储过程中引用对象的架构一样,
同时由于存储过程中只能是insert,update,delete,select,
而不能是trancate等操作,这就是所有权链,所以没有报错.
特别需要注意的是:以上的所有权链对于动态的SQL是无效的.
======================================================*/
exec dbo.usp_delete_wcE
--5.2这里改为truncate后,会报错
alter procedure dbo.usp_delete_wcE
as
truncate table dbo.wcE
go
--6.1使用execute as owner指定存储过程的任何调用,
--都是在存储过程架构的拥有者的安全上下文运行的
--这时再次以wclogin登录就可以执行存储过程而不会报错
alter procedure dbo.usp_delete_wcE
with execute as owner
as
truncate table dbo.wcE
go
--6.2以执行用户的权限来执行存储过程
--在以wclogin登录执行时会报错
alter procedure dbo.usp_delete_wcE
with execute as caller
as
truncate table dbo.wcE
go
--6.3以创建或最近一次更新存储过程的用户的权限来执行存储过程,
--在以wclogin登录执行时会报错
alter procedure dbo.usp_delete_wcE
with execute as self
as
truncate table dbo.wcE
go
--6.4以指定的用户的权限来执行存储过程,
--在以wclogin登录执行时显然不会报错
alter procedure dbo.usp_delete_wcE
with execute as 'dbo'
as
truncate table dbo.wcE
go
--6.5这个存储过程用了动态的SQL语句
create procedure dbo.w
as
exec('select * from dbo.wcE');
go
grant exec on dbo.w to wclogin
--在客户端以wclogin登录,执行时会报错
--拒绝了对对象'wcE'(数据库'wc',架构'dbo')的SELECT权限。
exec dbo.w
--1.函数
if exists(select * from sys.objects where name = 'f_splitSTR' and type = 'tf')
drop function dbo.f_splitSTR
go
create function dbo.f_splitSTR
(
@s varchar(8000), --要分拆的字符串
@split varchar(10) --分隔字符
)
returns @re table( --要返回的临时表
col varchar(1000) --临时表中的列
)
as
begin
declare @len int
set @len = LEN(@split) --分隔符不一定就是一个字符,可能是2个字符
while CHARINDEX(@split,@s) >0
begin
insert into @re
values(left(@s,charindex(@split,@s) - 1))
set @s = STUFF(@s,1,charindex(@split,@s) - 1 + @len ,'') --覆盖:字符串以及分隔符
end
insert into @re values(@s)
return --返回临时表
end
go
select * from dbo.f_splitSTR('123,456',',')
--2.存储过程
--创建类型
create type var_orgid as table
(
org_id numeric(10,0)
)
--创建存储过程
create proc usp_orgid
@orgid_table var_orgid readonly
as
select * from @orgid_table
go
--定义表变量
declare @orgid_table as var_orgid
insert into @orgid_table
values(123)
exec usp_orgid @orgid_table
sp_executesql存储过程,可以使动态语句重用,提高效率:
DECLARE @sumcount INT
DECLARE @tmpSQL NVARCHAR(1000)
DECLARE @TABLENAME VARCHAR(50)
SELECT @TABLENAME = ' sys.objects ' --表名变量赋值
SELECT @tmpSQL = 'select @sum = convert(varchar(50),count(object_id)) from ' + @TABLENAME
/*
参数1: 动态语句
参数2:定义的参数,这个参数就是在动态语句中引用到的变量@sum
参数3:指明了执行结果@sum的值,放到@sumcount参数里面,
这里需要特别注意的是,动态语句中的变量,和外面定义的变量,是不同的,
下面的语句,实际相当于:
declare @sum int
select @sum = convert(varchar(50),count(object_id))
from + 表名
所以,通过sp_executesql,可以把语句产生的结果输出到外面的变量,
这样我们才能知道输结果
*/
exec sp_executesql @tmpsql,
N'@sum int output',
@sumcount out
select @sumcount
try catch 错误处理
create proc proc_t(@i int)
as
select 5 * 1.0 / @i
go
--1.不会报错
begin try
begin tran
exec proc_t 1
select '执行成功'
commit tran
end try
begin catch
select '执行失败'
if @@trancount >0
rollback
end catch
--2.这次会报错,因为分母为0
begin try
begin tran
exec proc_t 0
select '执行成功'
commit tran
end try
begin catch
select '执行失败'
if @@trancount >0
rollback
end catch