存储过程(stored procedure)有时也称sproc,它是真正的脚本,更准确地说,它是批处理(batch),但都不是很确切,它存储与数据库而不是单独的文件中。
存储过程中有输入参数,输出参数以及返回值等。
一、创建存储过程
创建存储过程的方法和创建数据库中任何其他对象一样,除了他使用AS关键字外。存储过程的基本语法如下:
CREATE PROCEDURE|PROC <sproc name> [<parameter name> [schema.] <data type> [VARYING] [=<default value>] [OUT[PUT]] [READONLY] [,<parameter name> [schema.] <data type> [VARYING] [=<default value>] [OUT[PUT]] [READONLY] [,... ... ]] [WITH RECOMPILE | ENCRYPTION | [EXECUTE AS { CALLER | SELF | OWNER | <‘user name‘>}] AS <code> | EXTERNAL NAME <assembly name>.<assembly class>.<method>
在语法中,PROC是PROCEDURE的缩写,两个选项的意思一样。
在对存储过程命名完之后,接着是参数列表。参数是可选的。
关键字AS其后就是实际的代码。
简单的存储过程示例:
CREATE PROC spPerson AS SELECT * FROM Person
执行存储过程:
EXEC spPerson
查看结果:
二、ALTER修改存储过程
ALTER PROC和CREATE PROC的区别如下:
- ALTER PROC期望找到一个已有的存储过程,而CREATE则不是。
- ALTER PROC保留了存储过程上已经建立的任何权限。它在系统对象中保留了对象Id并允许保留依赖关系。例如,如果过程A调用过程B,如果删除并重建B,那么就不能在看到这两者间的依赖关系。如果使用ALTER,则依赖关系仍然存在。
- ALTER PROC在可能调用被修改的存储过程的其他对象上保留了任何依赖信息。
示例:
ALTER PROC spPerson AS SELECT * FROM Person WHERE Id = 45
三、删除存储过程
删除存储过程的语法最简单:
DROP PROC|PROCEDURE <sproc name>[;]
这样就完成了存储过程的删除。
四、参数化
如果存储过程没有办法接受一些数据,告诉其要完成的任务,则在大多数情况下,存储过程不会有太大帮助。例如,要删除一条数据,但却不指定Id,则存储过程也不知道要删除哪条,所以使用输入参数非常有必要。
1、声明参数
声明参数需要以下2到4部分的信息:
- 名称
- 数据类型
- 默认值
- 方向
其语法如下所示:
@parameter_name [AS] datatype [=default|NULL] [VARYING] [OUTPUT | OUT]
对于名称,有一组简单的规则。首先,它必须以@符号(和变量一样)开始。此外,除了不能内嵌空格外,其规则与普通变量规则相同。
数据类型和名称一样,必须像变量那样声明,采用SQL Server内置的或用户自定义的数据类型。
声明需要类型时需要注意,当声明CURSOR类型参数时,必须也使用VARYING和OUTPUT选项。同时,OUTPUT可以简写为OUT。
在默认值方面,参数与变量不同。对于同样的情况,变量一般初始化为NULL值,而参数不是。事实上,如果不提供默认则,则会假设参数是必须的,并且当调用存储过程时需要提供一个初始值。
一个需要传入参数的存储过程示例:
CREATE PROC spName @Name nvarchar(50) AS SELECT Name FROM Person WHERE Name LIKE @Name + ‘%‘;
执行存储过程:
EXEC spName ‘酒‘;
显示结果如下:
2、提供默认值
为了使参数是可选的,必须提供默认值。方法是在数据类型后在逗号之前添加"="符号和作为默认值的值。这样,存储过程的用户尅有决定对此参数不提供值或是提供他们自己的值。
创建一个存储过程如下:
CREATE PROC spName @Name nvarchar(50) = NULL AS IF @Name IS NOT NULL SELECT * FROM Person WHERE NAME = @Name ELSE SELECT * FROM Person WHERE Id = 45
执行如下语句:
EXEC spName EXEC spName ‘如意刀狼‘
输出结果如下:
3、输出参数
下面来看看一个获得OUTPUT参数的存储过程:
CREATE PROC InsertPerson @Id int OUTPUT --必须注明为OUTPUT AS INSERT INTO Person VALUES(‘刘备‘,22,190,‘不详‘,‘未婚‘,‘幼儿园‘,‘不详‘,4999999) SET @Id = @@IDENTITY
执行存储过程:
DECLARE @Id int --实际上,调用时名称可以不同,例如也可以为@Num,@i等等。 EXEC InsertPerson @Id OUTPUT --注意此处也要有OUTPUT SELECT @Id
显示结果如下:
对于存储过程本身以及调用脚本对它的使用,需要注意以下几点:
- 对于存储过程声明中的输出参数,需要使用OUTPUT关键字。
- 和声明存储过程时一样,调用存储过程时,必须使用OUTPUT关键字。这样就对SQL Server作了提前通知,告诉它参数所需要的特殊处理。但需要注意的是,如果忘记包含OUTPUT关键字,不会产生运行时错误,但是输出的值不会传入变量中(变量很可能是NULL)。
- 赋值给输出结果的变量不需要和存储过程中的内部参数拥有相同的名称。
- EXEC(或EXECUTE)关键字是必须的,因为对存储过程的调用并不是批处理要做的第一件事(如果存储过程的调用是批处理的第一件事,则可以不使用EXEC)。
五、返回值
返回值的用途非常广泛,例如,返回数据,标识值或是存储过程影响的行数等等。而其实际作用是返回值可用来确定存储过程执行的状态。
事实上,不管是否提供返回值,程序都会收到一个返回值。SQL Server默认会在完成存储过程时自动返回一个0值。
为了从存储过程向调用代码传递返回值,只需要使用RETURN语句。
RETURN [<integer value to return>]
要特别注意的是:返回值必须是整数。
关于RETURN语句,最重要的是知道它是无条件地从存储过程中退出的。无论运行到存储过程的哪个位置,在调用RETURN语句之后将不会执行任何一行代码。
这里的无条件,并不是说无论执行到代码的何处都将执行RETURN语句。相反,可以再存储过程中有多个RETURN语句。只有当代码的标准条件结构发出命令的时候,才会执行这些RETURN语句。一旦发生,就不能再退回了。
创建一个存储过程如下:
CREATE PROC spTestReturns AS DECLARE @MyMessage nvarchar(50); DECLARE @MyOtherMessage nvarchar(50); SELECT @MyMessage = ‘第一个RETURN‘; PRINT @MyMessage; RETURN; SELECT @MyOtherMessage = ‘第二个RETURN‘; PRINT @MyOtherMessage; RETURN;
执行存储过程,输出如下:
为了能捕获RETURN语句的值,需要在EXEC语句中把值赋给变量。例如:
DECLARE @Return int EXEC @Return = spTestReturns SELECT @Return
输出如下:
虽然简单但是还不错。当运行时,可以看到RETURN语句却是在运行其他代码前终止了代码运行。
如果总是返回0,那么执行成不成功都不知道,那么我们现在来改写下上面的存储过程,让其返回一个指定的值,以指示执行状态。
CREATE PROC spTestReturns AS DECLARE @MyMessage nvarchar(50); DECLARE @MyOtherMessage nvarchar(50); SELECT @MyMessage = ‘第一个RETURN‘; PRINT @MyMessage; RETURN 100; --将这里改成返回100 SELECT @MyOtherMessage = ‘第二个RETURN‘; PRINT @MyOtherMessage; RETURN;
执行之后,显示结果如下:
六、存储过程的优缺点
存储过程的主要优点包括以下几个方面:
- 使得需要过程式动作的进程可调用
- 安全性
- 性能
1、创建可调用的进程
很多人并没有意识到要充分使用存储过程,使其作为实现安全性的工具。和视图类似,可以创建一个返回记录集的存储过程而不用赋予用户访问底层数据表的权限。赋予某人执行一个存储过程的权限意味着他们可以在该存储过程中执行任何动作。不过要假设动作是在存储过程的上下文中执行的。
2、存储过程和性能
一般来说,存储过程有助于系统性能的提高。但是,如果设计的存储过程缺乏只能,那么它会使在其创建的进程变得非常缓慢。
存储过程的运行示意图如下:
首先运行CREATE PROC过程。这回解析查询以确保会实际运行这些代码。它与直接运行脚本的区别在于CREATE PROC命令可以利用所谓的延迟名称解析。延迟名称解析可以忽略一些对象还不存在的事实。
在创建了存储过程后,它将等待第一次执行。在那时,存储过程被优化,而查询计划被编译并且缓存到系统上。后续几次运行该存储过程时,除非通过使用WITH RECOMPILE选项指定,否则都会使用缓存的查询计划而不是创建一个新的查询计划。这意味着每次使用该存储过程时,存储过程都会跳过很多优化和编译工作。节省的确切时间取决于批处理的复杂性,批处理中表的大小,以及每个表上索引的数量。通常,节省的时间不是很多。但对于大多数场景来说可能是1秒或更少-但通过百分比可以计算出此区别(1秒比2秒快了100%)。当需要进行多次调用时或针对循环的情况,这一区别会变得更明显。
3、存储过程的不利方面
对于存储过程的不利之处要认识到的最重要的一点事,除非手动地干预(使用WITH RECOMPILE选项),否则只会在第一次运行存储过程的时候,或者当查询所涉及的表更新了统计信息时,才对存储过程进行优化。
这种"一次优化,多次使用"的策略节省了存储过程的时间,但是该策略也是一把双刃剑。如果查询是动态的(即是在使用EXEC命令时建立的),那么只会在第一次运行时对存储过程进行优化,但是会发现以后再也不这样了。简而言之,可能会使用错误的计划。
4、WITH RECOMPILE选项
可以利用存储过程提供的安全性代码和代码封装方面的好处,但还是忽略了预编译代码方面的影响。可以回避未使用正确的查询计划的问题,因为可以确保为特定一次运行创建新的计划。方法就是使用WITH RECOMPILE选项。
使用该选项的方式有两种:
1、可以在运行时包含WITH RECOMPILE。
EXEC spMySproc ‘1/1/2004‘ WITH RECOMPILE
这告诉SQL Server抛弃已有的执行计划并且创建一个新的计划-但只是这一次。也就是说,只是这次使用WITH RECOMPILE选项来执行存储过程。
也可以通过在存储过程中包含WITH RECOMPILE选项来使之变得更持久。如果使用这种方式,则在CREATE PROC或ALTER PROC语句中的AS语句前添加WITH RECOMPILE选项即可。
如果通过该选项创建存储过程,那么无论在运行时选择了其他什么选项,每次运行存储过程都会重新编译它。