使用游标(CURSOR)在需要一行一行处理时,游标十分有用。游标可以打开一个结果集合(按照指定的标准选择的行),并提供在结果集中一行一行处理的功能。基于游标的类型,可以对其进行回滚或者前进。
用DECLARE语句对游标进行声明,有两种方法可以指定一个游标。
SQL-92 语法
DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR
FOR select_statement
[ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ]
Transact-SQL 扩展语法
DECLARE cursor_name CURSOR
[ LOCAL | GLOBAL ]
[ FORWARD_ONLY | SCROLL ]
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ]
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ]
[ TYPE_WARNING ]
FOR select_statement
[ FOR UPDATE [ OF column_name [ ,...n ] ] ]
例1:定义一个游标,用于生成学生信息表中的所有记录。
DECLARE student_cursor CURSOR
FOR SELECT * FROM student
打开游标就是创建结果集,执行游标定义时指定的 Transact-SQL 语句填充游标。语法如下:
OPEN { { [ GLOBAL ] cursor_name } | cursor_variable_name }
例2:定义一个游标,用于显示学号为“110101”的学生选修课的详细情况,并打开游标。
DECLARE choice_Cursor CURSOR FOR
SELECT *
FROM choice
WHERE sno=’110101’
OPEN choice_Cursor
在从游标中读取数据的过程中,可以在结果集中的每一行上来回移动和处理。如果游标定义成了可滚动的(在声明时使用SCROLL关键字),则任何时候都可取出结果集中的任意行。对于非滚动的游标,只能对当前行的下一行实施取操作。结果集可以取到局部变量中。Fetch命令的语法如下:
FETCH [NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]
FROM [GLOBAL] cursor_name} | cursor_variable_name}
[INTO @variable_name ][,……n]]
例3:定义一个游标,返回学生信息表中所有的数据,打开游标,然后遍历学生信息表,直到找到学生名称为“张三丰”的记录为止,并且打印学生学号和学生名称。
DECLARE student_cursor CURSOR
FOR SELECT sno,sname FROM student
DECLARE @ID char(10), @Name char(30)
OPEN student_cursor
FETCH NEXT FROM student_cursor INTO @ID, @Name
WHILE @@fetch_status = 0
BEGIN
IF @Name = '张三丰'
BEGIN
PRINT '找到张三丰'
PRINT @ID+@Name
BREAK
END
FETCH NEXT FROM student_cursor INTO @ID, @Name
END
游标使用之后,要及时对它进行关闭和释放操作。CLOSE语句用来关闭游标并释放结果集。游标关闭之后,不能再执行FETCH操作。如果还需要使用FETCH语句,则要重新打开游标。语法如下:
CLOSE [GLOBAL] cursor_name | cursor_variable_name
游标确实不再需要之后,要释放游标。DEALLOCATE语句释放数据结构和游标所加的锁。语法如下:
DEALLOCATE [GLOBAL] cursor_name | cursor_variable_name
例4:
以例3为基础,要体现一个完整的使用游标的过程,在最后还应包括以下两句,对游标进行关闭和释放:
CLOSE student_cursor
DEALLOCATE student_cursor
存储过程是一段在服务器上执行的程序,它在服务器端对数据库记录进行处理,再把结果返回到客户端。通过使用存储过程,一方面可以利用服务器强大的计算能力和速度,另一方面避免把大量的数据从服务器下载到客户端,减少网络上传输量,服务器只需将计算结果传给客户端,因此可以提高客户端的工作效率。
包括系统存储过程和用户存储过程
系统存储过程中又分为一般系统存储过程和扩展存储过程。
存储过程的主要优点是可以提供对数据的轻松访问,客户端不需要知道复杂的数据结构或业务逻辑过程,只需要把查询指令通过存储过程发给服务器,服务器就能把需要的数据返回给用户。
存储过程分为两类,系统存储过程和用户自定义存储过程,SQL SERVER提供了大量的系统存储过程,用于管理SQL SERVER并显示有关数据库和用户的信息。
例5:用系统存储过程列出当前SQL SERVER实例中的所有数据库以及数据库的大小,用sp_databases。
EXEC sp_databases
例6:用系统存储过程列出当前数据中所有可以访问的表,用sp_tables
EXEC sp_tables
扩展存储过程是系统存储过程的一种,提供从 SQL Server 到外部程序的接口,以便进行各种维护活动。扩展存储过程的前缀是xp_
例7:用扩展存储过程显示当前目录下的全部文件,可以用xp_cmdshell实现。
EXEC master..xp_cmdshell ‘dir *.*’
可以用CREATE PROCEDURE语句来定义存储过程,语法如下:
CREATE PROC [ EDURE ] procedure_name [ ; number ]
[ { @parameter data_type }
[ VARYING ] [ = default ] [ OUTPUT ]
] [ ,...n ]
[ WITH
{ RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]
[ FOR REPLICATION ]
AS sql_statement [ ...n ]
procedure_name:新存储过程的名称。过程名必须符合标识符规则,且对于数据库及其所有者必须唯一。要创建局部临时过程,可以在 procedure_name 前面加一个编号符 (#procedure_name),要创建全局临时过程,可以在 procedure_name 前面加两个编号符 (##procedure_name)。完整的名称(包括 # 或 ##)不能超过 128 个字符。指定过程所有者的名称是可选的。
@parameter
过程中的参数。在 CREATE PROCEDURE 语句中可以声明一个或多个参数。用户必须在执行过程时提供每个所声明参数的值(除非定义了该参数的默认值)。存储过程最多可以有 2100 个参数。
使用 @ 符号作为第一个字符来指定参数名称。参数名称必须符合标识符的规则。每个过程的参数仅用于该过程本身;相同的参数名称可以用在其它过程中。默认情况下,参数只能代替常量,而不能用于代替表名、列名或其它数据库对象的名称。
data_type:参数的数据类型。所有数据类型(包括 text、ntext 和 image)均可以用作存储过程的参数。不过,cursor 数据类型只能用于 OUTPUT 参数。如果指定的数据类型为 cursor,也必须同时指定 VARYING 和 OUTPUT 关键字。
OUTPUT:表明参数是返回参数。该选项的值可以返回给 EXEC[UTE]。使用 OUTPUT 参数可将信息返回给调用过程。Text、ntext 和 image 参数可用作 OUTPUT 参数。使用 OUTPUT 关键字的输出参数可以是游标占位符。
AS:指定过程要执行的操作。
sql_statement:过程中要包含的任意数目和类型的 Transact-SQL 语句。但有一些限制。
存储过程的执行有两种方法:SQL Server Management Studio中可视化界面操作执行和TRANSACT-SQL语句执行。
(1)SQL Server Management Studio中可视化界面操作执行存储过程
在对象资源管理器中,展开students数据库,在“可编程性”中展开“存储过程”树型目录,右击proc_stu1,在快捷菜单上,选择“执行存储过程”命令,运行该存储过程,如图所示。
(2)通过TRANSACT-SQL语句的EXEC命令执行一个已定义的存储过程
语法格式:
[ EXEC [ UTE ] ][@return_status = ]
{ procedure_name [;number ] | @procedure_name_var }
[ [ @parameter = ] {value |@variable [ OUTPUT]|[DEFAULT ]}]
[ ,...n ]
例8:创建一个存储过程proc_jsjstu,在students数据库中,实现查找“计算机系”学生的信息,运行结果如图7-7所示。
注:在学生学号中的第5、6位是系部编号,系部编号与系部名称对应关系见表。
EXEC proc_stu1
GO
如果要执行的存储过程不是批处理的第一句,则需要加上 exec 或 execute,如果是批处理的第一句,则可以省略 exec 或 execute
USE students
GO
CREATE PROCEDURE proc_jsjstu @xbbh varchar(4)
AS
SELECT *
FROM stu
WHERE SUBSTRING(id,5,2)=@xbbh
GO
EXEC proc_jsjstu '02'
例9:创建一个存储过程,该存储过程能够实现根据系部的编号查询出系部中男生、女生的人数。
USE students
GO
CREATE PROCEDURE proc_countsex @xbbh varchar(4)
AS
BEGIN
SELECT sex as '性别',count(id) as '人数合计'
FROM student
WHERE substring(id,5,2)=@xbbh
GROUP BY sex
END
EXEC proc_countsex '02'
1.带输出参数的存储过程
用OUTPUT 可以声明一个带返回值参数,可以将信息返回给调用过程,如果某个参数在传输到存储过程中时被定义成OUTPUT,则对该参数的任何修改在退出存储之后仍然有效。
2.创建带输出参数的存储过程
通过在创建存储过程的语句中定义输出参数,可以创建带输出参数的存储过程。执行该存储过程,可以返回一个或多个值。具体语法如下:
CREATE PROC [EDURE] procedure_name [;number] /*定义存储过程名*/
[{@parameter data_type } /*定义参数的类型*/
[VARYING] [=default][OUTPUT]]
/* OUTPUT关键字指定参数的属性为输出参数*/
[ ,...n ]
[WITH{RECOMPILE|ENCRYPTION|RECOMPILE,ENCRYPTION}]
/*定义存储过程的处理方式*/
[ FOR REPLICATION ]
AS sql_statements /*执行的操作*/
例10 在students学生数据库中,创建一个存储过程proc_ testOutput,要求实现返回成绩在@p1与@p3之间的学生人数,其中,@p1与@p3是输入参数,用于指定分数段。
CTEATE PROCEDURE dbo.proc_testOutput
(
@p1 int ,
@p2 int OUTPUT,
@p3 int
)
AS
BEGIN
select @p2 = count(*) from stu where score between @p1 and @p3
RETURN @@rowcount
PRINT @P2
END
--这个存储过程返回两个值,一个是output型参数@p2,调用存储过程后,返回结果存放在@p2中。
--另外一个是由return值@@rowcount(语句所影响的行数)
SQL Server 2008启动时可以自动执行一个或多个存储过程。这些存储过程必须由系统管理员创建,并在 sysadmin 固定服务器角色下作为后台过程执行。这些过程不能有任何输入参数,也不能从过程中返回任何结果集。
若要创建启动存储过程,必须作为 sysadmin 固定服务器角色的成员登录,并在 master 数据库中创建存储过程。
使用Transact-SQL命令方式设置自动执行存储过程:将存储过程创建在master数据库中,使用系统存储过程sp_procoption可以将现有存储过程设置为自动执行过程或停止自动执行。
(1)先在master数据库中创建存储过程
CREATE PROCEDURE pro_viewstudent
AS
SELECT * FROM spt_values
GO
(2)使用系统存储过程sp_procoption可以将现有存储过程设置为自动执行过程
USE master
GO
EXEC sp_procoption 'pro_viewstudent','startup','on'
--第一个参数是存储过程名
--第二个参数startup,该值表示设置存储过程的自动执行状态
--第三个参数可以设置为开(true或on)还是关(false或off)
1.重命名存储过程
(1)使用SQL Server Management Studio重命名存储过程
在对象资源管理器中,展开"数据库"文件夹,再展开“可编程性”的“存储过程”,找到要修改名字的存储过程,然后右击打开快捷方式,选择“重命名”命令,即可完成存储过程的重命名,如图所示。
2.修改存储过程
(1)使用SQL Server Management Studio修改存储过程
(2)使用Transact-SQL语句修改存储过程
使用ALTER PROCEDURE语句修改存储过程,语法格式:
ALTER PROC[EDURE]存储过程名[;下标]
[{@形参 数据类型}
[VARYING][=默认值][OUTPUT] ][,...n1]
[WITH { RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION } ]
AS Transact-SQL 语句 [...n2] /*执行的操作*/
各参数含义与CREATE PROCEDURE命令相同。从形式看,该语句与CREATE PROCDURE语句的主要差别仅在开头的关键字不同(一为CREATE,另一为ALTER),但ALTER PROCEDURE语句不会更改权限和启动属性,即如果我们先删除某存储过程,再用CREATE PROCDURE语句创建与它同名的存储过程,虽然两者的语句一致,但原先该存储过程的权限以及启动属性将不复存在。
而用ALTER PROCEDURE更改存储过程后,该过程的权限和启动属性保持不变。另外,如果原来的过程定义是用 WITH ENCRYPTION 或 WITH RECOMPILE 创建的,那么只有在 ALTER PROCEDURE 中也包含这些选项时,这些选项才有效。
例11修改存储过程proc_scorepass,在students数据库中,要求根据该学生所选课程编号为“0201305”课程的成绩显示提示信息,即如果成绩在90分以上,显示“优秀”,在80分以上,显示“良好”,在70分以上,显示“中等”,在60分以上,显示“及格”,否则显示“不及格!”。
USE students
GO
ALTER PROCEDURE proc_scorepass
AS
SELECT *,等级=case
WHEN score>=90 THEN '优秀'
WHEN score>=80 THEN '良'
WHEN score>=70 THEN '中'
WHEN score>=60 THEN '及格'
ELSE '不及格'
END
FROM grade
WHERE sno='0203105'
EXEC proc_scorepass
3.删除存储过程
(1)使用SQL Server Management Studio删除存储过程
在SQL Server Management Studio中,找到要删除的存储过程所在的数据库,展开数据库,展开“可编程性”目录树,展开“存储过程”,选中所要删除的存储过程,然后单击右键打开快捷菜单,点击“删除”命令,如图所示。
(2)使用TRANSACT-SQL语句删除
语法
DROP PROCEDURE { procedure } [ ,...n ]
参数
procedure 是要删除的存储过程或存储过程组的名称
n 是表示可以指定多个过程的占位符。
例12.将“students”数据库中存储过程“proc_scorepass”删除
USE students
GO
DROP PROCEDURE proc_scorepass
如果用户不想让其他人查看存储过程的定义文本,可以在定义存储过程时,对其进行加密。加密存储过程的关键字是:WITH ENCRYPTION,使用 ENCRYPTION 可防止将过程作为 SQL Server 复制的一部分发布。
在存储过程中进行错误处理是非常重要的。在访问存储过程的时候发生错误,可以通过@@ERROR系统函数返回错误代码。系统变量@@error在执行每一个Transact SQL语句之后都会得到一个值。对于成功的执行,@@error的值为0,如果出现错误,则@@error中将包含错误信息。
可以用系统存储过程sp_addmessage自定义错误信息,语法如下:
sp_addmessage [ @msgnum =] msg_id ,
[ @severity = ] severity ,
[ @msgtext = ] 'msg'
[ , [ @lang = ] 'language' ]
[ , [ @with_log = ] 'with_log' ]
[ , [ @replace = ] 'replace' ]
错误的有效级别为 1 到 25。只有系统管理员可以使用从 19 到 25 之间的严重级别添加消息。用户自定义的错误信息从50001 开始,例如可以自定义一个错误信息:
EXEC sp_addmessage 50001, 16, '发生错误,请重新执行该语句'
用RAISERROR可以触发错误信息,客户端可以从 sysmessages 表中检索条目,或者使用用户指定的严重度和状态信息动态地生成一条消息。这条消息在定义后就作为服务器错误信息返回给客户端。
RAISERROR语法如下:
RAISERROR ( { msg_id | msg_str } { , severity , state }
[ , argument [ ,...n ] ] )
[ WITH option [ ,...n ] ]
例如触发上例中自定义的消息,可以用如下语句:
RAISERROR 50001, 1