(文章中的sql语句基于sql server)
目录
存储过程:
1.CREATE PROCEDURE语句
EXEC语句执行一个已定义的存储过程
【强化练习】
事务:
L1.事务处理
1.开始事务
2.结束事务
3.撤销事务
4.回滚事务
【强化练习】
触发器:
【强化练习】
创建DDL触发器 语法格式:
ALTER TRIGGER语句
DROP TRIGGER语句
函数
1.CREATE FUNCTION语句
【强化练习】
游标
1.游标的使用
2.打开游标
3.读取数据
4.关闭游标
5.删除游标
使用存储过程的优点如下:
(1)存储过程在服务器端运行,执行速度快;
(2)存储过程执行一次后,就驻留在高速缓冲存储器,在以后的操作中,只需从高速缓冲存储器中调用已编译好的二进制代码执行,提高了系统性能;
(3)使用存储过程可以完成所有数据库操作,并可通过编程方式控制对数据库信息访问的权限,确保数据库的安全;
(4)自动完成需要预先执行的任务,存储过程可以在SQL Server启动时自动执行,而不必在系统启动后再进行手工操作,大大方便了用户的使用,可以自动完成一些需要预先执行的任务。
CREATE PROCEDURE语句用于在当前数据库中创建一个存储过程,语法格式如下:
CREATE { PROC | PROCEDURE } <存储过程名> /*定义过程名*/
[ { @<参数名> <数据类型> } /*定义参数的类型*/
[ VARYING ] [ = default ] [ OUT | OUTPUT ] [READONLY] /*定义参数的属性*/
][ ,...n ]
AS
{
[ ...n ] /*执行的操作*/
}
[;]
说明: (1)创建存储过程的语句是CREATE PROCEDURE或CREATE PROC,两者同义。对于指定的<存储过程名>,必须符合标识符规则,且对于数据库及所在架构必须唯一。这个名称应当尽量避免取与系统内置函数相同的名称,否则会发生错误。另外,也应当尽量避免使用“sp_”作为前缀,此前缀是由SQL Server指定系统存储过程的。
(2)@符号作为第一个字符来指定参数名称,参数名必须符合标识符规则。创建存储过程时,可声明一个或多个参数。执行存储过程时应提供相应的参数,除非定义了该参数的默认值,默认参数值只能为常量。参数的<数据类型>可以是SQL Server支持的任何类型,但cursor类型只能用于OUTPUT参数,如果指定参数的数据类型为cursor,必须同时指定VARYING和OUTPUT关键字,OUT与OUTPUT关键字意义相同。
(3)VARYING关键字指定作为输出参数支持的结果集。该参数由存储过程动态构造,其内容可能发生改变,仅适用于cursor参数。
(4)default指定存储过程输入参数的默认值,默认值必须是常量或NULL。如果存储过程使用了带LIKE关键字的参数,默认值中可以包含通配符(%、_、[]和[^]),如果定义了默认值,执行存储过程时根据情况可不提供实参。
(5)OUTPUT指示参数为输出参数,输出参数可以从存储过程返回信息。READONLY指定不能在存储过程的主体中更新或修改参数。
(6)存储过程体中可以包含一条或多条T-SQL语句,除了DCL、DML与DDL命令外,还能包含过程式语句,如变量的定义与赋值、流程控制语句等。
如果已经定义的存储过程需要修改,可以使用ALTER PROCEDURE语句,语法格式如下:
ALTER { PROC | PROCEDURE } <存储过程名>
[ { @<参数名> <数据类型> }
[ VARYING ] [ = default ] [ OUT | OUTPUT ]
][ ,...n ]
AS
{
[ ...n ]
}
[;]
当不再使用一个存储过程时,可以把它从数据库中删除。使用DROP PROCEDURE语句可永久地删除存储过程。在此之前,必须确认该存储过程没有任何依赖关系。语法格式:
DROP { PROC | PROCEDURE } <存储过程名> [ ,...n ]
通过EXEC语句可以执行一个已定义的存储过程,EXEC是EXECUTE的简写。语法格式如下:
[ { EXEC | EXECUTE } ]
{ [@return_status = ] <存储过程名>
[ [ @<参数名>= ] {<值> | @<变量名>[ OUTPUT ] | [ DEFAULT ] }][ ,...n ]
}
[;]
说明: (1)@return_status:为可选的整型变量,保存存储过程的返回值。 (2)存储过程名后面可以指定定义存储过程时指定的参数名和值,如果省略了参数名,则后面的值的顺序要与定义参数的顺序一致。在使用“@<参数名>=<值>”格式时,参数名和值不必按在存储过程中定义的顺序提供。 (3)当定义的参数为输出参数时,使用一个局部变量保存OUTPUT参数返回的值,使用OUTPUT关键字用于说明该参数输出参数。DEFAULT关键字表示不提供实参,而是使用对应的默认值。 (4)EXEC语句除了可以执行存储过程外,还可以调用标量函数。调用的方法与执行存储过程类似,但不存在输出参数。 (5)执行存储过程时若语句是批处理中的第一个语句,则不一定要指定EXEC关键字
(1)从学生成绩管理数据库PXSCJ中返回081101号学生的成绩情况。使用存储过程实现,该存储过程不使用任何参数。
USE PXSCJ
GO
CREATE PROCEDURE student_info
AS
SELECT *
FROM CJB
WHERE XH= '081101'
GO
存储过程定义后,执行存储过程student_info: EXECUTE student_info
如果该存储过程是批处理中的第一条语句,则可使用: student_info
(2)从PXSCJ数据库的三个表中查询某人指定课程的成绩和学分。使用存储过程实现,该存储过程接受与传递参数精确匹配的值。
USE PXSCJ
GO
CREATE PROCEDURE student_info1 @name char (8), @cname char(16)
AS
SELECT a.XH, XM, KCM, CJ, t.XF
FROM XSB a INNER JOIN CJB b
ON a.XH = b.XH INNER JOIN KCB t
ON b.KCH= t.KCH
WHERE a.XM=@name and t. KCM=@cname
执行存储过程student_info1: EXECUTE student_info1 '王林', '计算机基础'
以下命令的执行结果与上面相同: EXECUTE student_info1 @name='王林', @cname='计算机基础'
或者: DECLARE @proc char(20) SET @proc= 'student_info1' EXECUTE @proc @name='王林', @cname='计算机基础'
(3)创建一个存储过程do_insert,作用是向XSB表中插入一行数据。创建另外一个存储过程do_action,在其中调用第一个存储过程,并根据条件处理该行数据,处理后输出相应的信息。
第一个存储过程:
CREATE PROCEDURE dbo.do_insert
AS
INSERT INTO XSB VALUES('091201', '陶伟', 1, '1990-03-05', '软件工程',50, NULL);
第二个存储过程:
CREATE PROCEDURE do_action @X bit, @STR CHAR(8) OUTPUT
AS
BEGIN
EXEC do_insert
IF @X=0
BEGIN
UPDATE XSB SET XM='刘英', XB=0 WHERE XH='091201'
SET @STR='修改成功'
END
ELSE
IF @X=1
BEGIN
DELETE FROM XSB WHERE XH='091201'
SET @STR='删除成功'
END
END
接下来执行存储过程do_action来查看结果:
DECLARE @str char(8)
IF EXISTS(SELECT * FROM XSB WHERE XH='091201') /*判断是否存在该行数据*/
DELETE FROM XSB WHERE XH='091201'
EXEC dbo.do_action 1, @str OUTPUT
SELECT @str
(4)从三个表(a,b,c)的连接中返回指定姓氏的学生学号、姓名、所选课程名称及该课程的成绩。使用带有通配符参数的存储过程实现,该存储过程在参数中使用了模式匹配,如果没有提供参数,则使用预设的默认值。
CREATE PROCEDURE st_info @name varchar(30) = '李%'
AS
SELECT a.XH, a.XM, c.KCM, b.CJ
FROM XSB a INNER JOIN CJB b
ON a.XH =b.XH INNER JOIN KCB c
ON c.KCH= b.KCH
WHERE XM LIKE @name
GO
按照默认参数执行存储过程: EXECUTE st_info /*参数使用默认值*/
5)删除PXSCJ数据库中的student_info存储过程。
IF EXISTS(SELECT name FROM sysobjects WHERE name='student_info')
DROP PROCEDURE student_info
事务在SQL Server中相当于一个执行单元,它由一系列T-SQL语句组成。 这个单元中的每个SQL语句是互相依赖的,而且单元作为一个整体是不可分割的。 如果单元中的一个语句不能完成,整个单元就会回滚(撤销),所有影响到的数据将返回到事务开始以前的状态。因而,只有事务中的所有语句都成功地执行才能说这个事务被成功地执行。
在形式上,事务是由ACID属性标识的。术语“ACID”是一个简称,每个事务的处理必须满足ACID原则,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。
(1)原子性。原子性意味着每个事务都必须被认为是一个不可分割的单元。
(2)一致性。不管事务是完全成功完成还是中途失败,当事务使系统中的所有数据处于一致的状态时存在一致性。
(3)隔离性。隔离性是指每个事务在它自己的空间发生,和其他发生在系统中的事务隔离,而且事务的结果只有在它完全被执行时才能看到。
(4)持久性。持久性意味着一旦事务执行成功,在系统中产生的所有变化将是永久的。即使系统崩溃,一个提交的事务仍然存在。当一个事务完成,数据库的日志已经被更新时,持久性就开始发生作用。
SQL Server中的事务可以分为两类:系统提供的事务和用户定义的事务。 系统提供的事务是在执行某些T-SQL语句时,一条语句就构成了一个事务,这些语句包括:ALTER TABLE、CREATE、DELETE、DROP、FETCH、GRANT、INSERT、OPEN、REVOKE、SELECT、UPDATE、TRUNCATE TABLE。
例如执行如下创建表的语句:
CREATE TABLE xxx
(
f1 int NOT NULL,
f2 char(10) NOT NULL,
f3 varchar(30) NULL
)
在SQL Server中,开始一个事务可以使用BEGIN TRANSACTION语句。语法格式如下:
BEGIN { TRAN | TRANSACTION } <事务名>
[ WITH MARK [ 'description' ] ]
]
[ ; ]
COMMIT TRANSCATION语句是提交语句,它使得自从事务开始以来所执行的所有数据修改成为数据库的永久部分,也标志一个事务的结束,其语法格式如下:
COMMIT { TRAN | TRANSACTION } <事务名> [ ; ]
标志一个事务的结束也可以使用COMMIT WORK语句。语法格式为:COMMIT [WORK]
若要结束一个事务,可以使用ROLLBACK TRANSACTION语句。它使得事务回滚到起点,撤销自最近一条BEGIN TRANSACTION语句以后对数据库的所有更改,同时也标志了一个事务的结束。 语法格式:
ROLLBACK { TRAN | TRANSACTION } <事务名> [ ; ]
ROLLBACK TRANSACTION语句不能在COMMIT语句之后。另外,一条ROLLBACK WORK语句也能够撤销一个事务,功能与BEGIN TRANSACTION语句一样。但ROLLBACK TRANSACTION语句接受用户定义的事务名称。 语法格式:
ROLLBACK [ WORK ] [ ; ]
ROLLBACK TRANSACTION语句除了能够撤销整个事务,还可以使事务回滚到某个点,不过在这之前需要使用SAVE TRANSACTION语句来设置一个保存点。 SAVE TRANSACTION的语法格式:
SAVE { TRAN | TRANSACTION } <保存点名称> [ ; ]
SAVE TRANSACTION语句会向已命名的保存点回滚一个事务。如果在保存点被设置后,当前事务对数据进行了更改,则这些更改会在回滚中被撤销。语法格式为:
ROLLBACK { TRAN | TRANSACTION } <保存点名称>[ ; ]
其中,<保存点名称>是SAVE TRANSACTION语句中已经设置的名称。在事务中允许有重复的保存点名称,但指定保存点名称的ROLLBACK TRANSACTION语句只将事务回滚到使用该名称的最近的SAVE TRANSACTION。
下面几个语句说明了有关事务的处理过程:
1. BEGIN TRANSACTION mytran1
2. UPDATE …
3. DELETE…
4. SAVE TRANSACTION S1
5. DELETE…
6. ROLLBACK TRANSACTION S1;
7. INSERT…
8. COMMIT TRANSACTION
说明:在以上语句中,第1行语句开始了一个事务mytran1;第2、3行语句对数据进行了修改,但没有提交;第4行设置了一个保存点S1;第5行删除了数据,但没有提交;第6行将事务回滚到保存点S1,这时第5行所做修改被撤销了;第7行添加了数据;第8行结束了这个事务mytran1,这时只有第2、3、7行对数据库做的修改被持久化。
定义一个事务,向PXSCJ数据库的XSB表添加一行数据,然后删除该行数据。但执行后,新插入的数据行并没有删除,因为事务中使用了ROLLBACK语句将操作回滚到保存点My_sav,即删除前的状态。
BEGIN TRANSACTION My_tran
USE PXSCJ
INSERT INTO XSB
VALUES('091115', '胡新华', 1, '1991-06-27', '计算机', 50, NULL)
SAVE TRANSACTION My_sav
DELETE FROM XSB WHERE XH='091115'
ROLLBACK TRAN My_sav
COMMIT WORK
GO
执行完上述语句后使用SELECT语句查询XSB表中的记录:
SELECT * FROM XSB WHERE XH='091115' //可以查到因为回滚了delete语句(在最近的保存点与rollback之间)
结果如下:
说明: (1)触发器名必须符合标识符规则,并且在数据库中必须唯一。
(2)触发器所基于的对象。ON关键字后面指定在其上执行触发器的表,也可以称为触发器表。除了表以外,视图上也可以定义触发器。使用WITH ENCRYPTION选项可以对CREATE TRIGGER语句的文本进行加密。
(3)触发器激活的时机。 ① AFTER用于说明触发器在指定操作都成功执行后触发,如AFTER INSERT表示向表中插入数据时激活触发器。不能在视图上定义AFTER触发器。如果指定FOR关键字则也创建AFTER触发器。一个表可以创建多个给定类型的AFTER触发器。
(4)激活触发器的语句类型。{[DELETE] [,] [INSERT] [,] [UPDATE]}指定激活触发器的语句的类型,必须至少指定一个选项。在触发器定义中允许使用上述选项的任意顺序组合。INSERT表示将新行插入表时激活触发器。UPDATE表示更改某一行时激活触发器。DELETE表示从表中删除某一行时激活触发器。
(5)触发器执行的T-SQL语句,可以有一条或多条语句,用于指定DML触发器触发后将要执行的动作。
(6)触发器中使用的特殊表。执行触发器时,系统创建了两个特殊的临时表inserted表和deleted表。当向表中插入数据时,INSERT触发器触发执行,新的记录插入到触发器表和inserted表中。deleted表用于保存已从表中删除的记录,当触发一个DELETE触发器时,被删除的记录存放到deleted表中。
(7)创建DML触发器的说明。 ① CREATE TRIGGER 语句必须是批处理中的第一条语句,并且只能应用到一个表中。 ② DML触发器只能在当前的数据库中创建,但可以引用当前数据库的外部对象。 ③ 创建DML触发器的权限默认分配给表的所有者。 ④ 在同一CREATE TRIGGER语句中,可以为多种操作(如INSERT和UPDATE)定义相同的触发器操作。 ⑤ 不能对临时表或系统表创建DML触发器。 ⑥ 对于含有DELETE或UPDATE操作定义的外键表,不能使用INSTEAD OF DELETE和INSTEAD OF UPDATE触发器。 ⑦ TRUNCATE TABLE语句虽然能够删除表中记录,但它不会触发DELETE触发器。 ⑧ 在触发器内可以指定任意的SET语句,所选择的SET选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。 ⑨ DML触发器最大的用途是返回行级数据的完整性,而不是返回结果。所以应当尽量避免返回任何结果集。 ⑩ DML触发器中不能包含以下语句:ALTER DATABASE、CREATE DATABASE、DROP DATABASE、RESTORE DATABASE等。
(1)创建一个表table1,其中只有一列a。在表上创建一个触发器,每次插入操作时,将变量@str的值设为“TRIGGER IS WORKING”并显示。
USE PXSCJ
GO
CREATE TABLE table1(a int)
GO
CREATE TRIGGER table1_insert ON table1
AFTER INSERT
AS
BEGIN
DECLARE @str char(50)
SET @str='TRIGGER IS WORKING'
PRINT @str
END
向table1中插入一行数据: INSERT INTO table1 VALUES(10) 执行结果如下所示:
(2)创建触发器,当向CJB表中插入一个学生的成绩时,将XSB表中该学生的总学分加上添加的课程的学分。
CREATE TRIGGER cjb_insert ON CJB
AFTER INSERT
AS
BEGIN
DECLARE @num char(6), @kc_num char(3)
DECLARE @xf int
SELECT @num=XH, @kc_num=KCH FROM inserted
SELECT @xf=XF FROM KCB WHERE KCH=@kc_num
UPDATE XSB SET ZXF=ZXF+@xf WHERE XH=@num
PRINT '修改成功'
END
(3)创建触发器,当修改XSB表中的学号时,同时也要将CJB表中的学号修改成相应的学号(假设XSB表和CJB表之间没有定义外键约束)
CREATE TRIGGER xsb_update ON XSB
AFTER UPDATE
AS
BEGIN
DECLARE @old_num char(6), @new_num char(6)
SELECT @old_num=XH FROM deleted
SELECT @new_num=XH FROM inserted
UPDATE CJB SET XH=@new_num WHERE XH=@old_num
END
接着修改XSB表中的一行数据,并查看触发器执行结果:
UPDATE XSB SET XH='081120' WHERE XH='081101'
GO
SELECT * FROM CJB WHERE XH='081120'
(4)创建DELETE触发器。在删除XSB表中的一条学生记录时将CJB表中该学生的相应记录也删除。
CREATE TRIGGER xsb_delete
ON XSB AFTER DELETE
AS
BEGIN
DELETE FROM CJB
WHERE XH IN(SELECT XH FROM deleted)
END
(5)在KCB表中创建UPDATE和DELETE触发器,当修改或删除KCB表中的课程号字段时,同时修改或删除CJB表中的该课程号。
CREATE TRIGGER kcb_trig
ON KCB AFTER UPDATE, DELETE
AS
BEGIN
IF (UPDATE(KCH))
UPDATE CJB SET KCH=(SELECT KCH FROM inserted)
WHERE KCH=(SELECT KCH FROM deleted)
ELSE
DELETE FROM CJB
WHERE KCH IN(SELECT KCH FROM deleted)
END
(6)创建表table2,值包含一列a,在表中创建INSTEAD OF INSERT触发器,当向表中插入记录时显示相应消息。
USE PXSCJ
GO
CREATE TABLE table2(a int)
GO
CREATE TRIGGER table2_insert
ON table2 INSTEAD OF INSERT
AS
PRINT 'INSTEAD OF TRIGGER IS WORKING'
向表中插入一行数据: INSERT INTO table2 VALUES(10)
CREATE TRIGGER <触发器名称>
ON { ALL SERVER | DATABASE }
[ WITH ENCRYPTION ]
{ FOR | AFTER } { event_type | event_group } [ ,...n ]
AS
{
[ ; ] [ ...n ]
}
说明: ALL SERVER | DATABASE:ALL SERVER关键字是指将当前DDL触发器的作用域应用于当前服务器,DATABASE指将当前DDL触发器的作用域应用于当前数据库。 event_type:执行之后将导致触发DDL触发器的T-SQL语句事件的名称。当ON关键字后面指定DATABASE选项时使用该名称。值得注意的是,每个事件对应的T-SQL语句有一些修改,如要在使用CREATE TABLE语句时激活触发器,AFTER关键字后面的名称为CREATE_TABLE,在关键字之间包含下画线(“_”)。event_type选项的值可以是CREATE_TABLE、ALTER_TABLE、DROP_TABLE、CREATE_USER、CREATE_VIEW等。 event_group:预定义的T-SQL语句事件分组的名称。ON关键字后面指定为ALL SERVER选项时使用该名称,如CREATE_DATABASE、ALTER_DATABASE等。
【强化练习】创建PXSCJ数据库作用域的DDL触发器,当删除一个表时,提示禁止该操作,然后回滚删除表的操作。
USE PXSCJ
GO
CREATE TRIGGER safety
ON DATABASE
AFTER DROP_TABLE
AS
PRINT '不能删除该表'
ROLLBACK TRANSACTION
ALTER TRIGGER语句用于修改已经定义的触发器。 (1)修改DML触发器的语法格式:
ALTER TRIGGER <触发器名称>
ON <表名> /*指定操作对象*/
[ WITH ENCRYPTION ] /*说明是否采用加密方式*/
{ FOR |AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
AS
{
[ ; ] [ ...n ]
}
(2)修改DDL触发器的语法格式:
ALTER TRIGGER <触发器名称>
ON { ALL SERVER | DATABASE }
[ WITH ENCRYPTION ]
{ FOR | AFTER } { event_type | event_group } [ ,...n ]
AS
{
[ ; ] [ ...n ]
}
【强化练习】修改PXSCJ数据库中在XSB表上定义的触发器xsb_delete,将其修改为UPDATE触发器。
USE PXSCJ
GO
ALTER TRIGGER xsb_delete ON XSB
FOR UPDATE
AS
PRINT '执行的操作是修改'
触发器本身是存在表中的,因此,当表被删除时,表中的触发器也将一起被删除。删除触发器使用DROP TRIGGER语句。 语法格式:
DROP TRIGGER <触发器名称> [ ,...n ] [ ; ] /*删除DML触发器*/
DROP TRIGGER <触发器名称> [ ,...n ] ON { DATABASE | ALL SERVER }[ ; ]/*删除DDL触发器*/
【强化练习】 (1)删除DML触发器xsb_delete。
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'xsb_delete')
DROP TRIGGER xsb_delete
说明:DML触发器创建后名称一般保存在系统表sysobjects中,在删除前可以先判断该触发器的名称是否存在。
(2)删除DDL触发器safety:
DROP TRIGGER safety ON DATABASE
1.标量函数
(1)标量函数的定义。定义标量函数的语法格式如下:
CREATE FUNCTION [<架构名>.] <函数名> /*函数名部分*/
( @<参数名> [AS] <数据类型> [=default] ) [READONLY][ ,...n ]) /*形参定义部分*/
RETURNS <返回值类型> /*返回参数的类型*/
[ WITH <选项> [,...n]] /*函数选项定义*/
[ AS ]
BEGIN
<函数体> /*函数体部分*/
RETURN <标量表达式> /*返回语句*/
END
[ ; ]
其中:
<选项 >::=
{
[ ENCRYPTION ]
| [ SCHEMABINDING ]
| [ RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT ]
}
说明:
① 自定义函数名必须符合标识符的规则,对其架构来说,该名称在数据库中必须是唯一的。
② CREATE FUNCTION语句中可以声明一个或多个参数,用@符号作为第一个字符来指定形参名,每个函数的参数局部于该函数。参数的数据类型可为系统支持的基本标量类型。[ = default ]可以设置参数的默认值。如果定义了default值,则无须指定此参数的值即可执行函数。READONLY选项用于指定不能在函数定义中更新或修改参数。
③ RETURNS语句用于指定用户自定义函数的返回值类型。可以是SQL Server支持的基本标量类型,但text、ntext、image和timestamp除外。使用RETURN语句函数将返回<标量表达式>的值。
④ 使用WITH子句可以设置标量函数的选项,有以下几种:
ENCRYPTION:用于指定SQL Server加密包含CREATE FUNCTION语句文本。
SCHEMABINDING:用于指定将函数绑定到它所引用的数据库对象。
RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT:如果指定前者则当传递的参数为NULL时,函数将不执行函数体,返回NULL。
(2)标量函数的调用。当调用用户定义的标量函数时,必须提供至少由两部分组成的名称(架构名.函数名)。可用以下方式调用标量函数:
① 在SELECT语句中调用。调用形式:架构名.函数名(实参1,…,实参n)。实参可为已赋值的局部变量或表达式。
② 利用EXEC语句执行。用EXEC(EXECUTE)语句调用用户自定义函数时,参数的标识次序与函数定义中的参数标识次序可以不同。
调用形式: 架构名.函数名 实参1,…,实参n
或: 架构名.函数名 形参名1=实参1,…, 形参名n=实参n
创建用户定义函数,实现计算全体学生某门功课的平均成绩的功能。
USE PXSCJ
GO
CREATE FUNCTION average(@cnum char(20))
RETURNS int
AS
BEGIN
DECLARE @aver int
SELECT @aver=
(
SELECT avg(CJ)
FROM CJB
WHERE KCH=@cnum
GROUP BY KCH
)
RETURN @aver
END
GO
使用EXEC语句调用自定义函数average:
DECLARE @course char(20)
DECLARE @aver int
EXEC @aver = dbo.average @cnum = '101'/*通过EXEC调用函数,将返回值赋给局部变量*/
SELECT @aver AS '101课程的平均成绩' /*显示局部变量的值*/
执行结果如下所示:
SQL Server对游标的使用要遵循:声明游标->打开游标->读取数据->关闭游标->删除游标。
1.声明游标 T-SQL中声明游标使用DECLARE CURSOR语句,该语句有两种格式,分别支持SQL-92标准和T-SQL扩展的游标声明。
(1)SQL-92语法。语句格式:
DECLARE <游标名> [ INSENSITIVE ] [ SCROLL ] CURSOR
FOR
说明: 游标名是与某个查询结果集相联系的符号名,要符合SQL Server标识符命名规则。
INSENSITIVE:指定系统将创建供所定义的游标使用的数据的临时复本,对游标的所有请求都从tempdb数据库中的该临时表中得到应答,因此,在对该游标进行提取操作时返回的数据不反映对基表所做的修改,并且该游标不允许修改。
SCROLL:说明所声明的游标可以前滚、后滚,可使用所有的提取选项(FIRST、LAST、PRIOR、NEXT、RELATIVE、ABSOLUTE)。
READ ONLY:说明所声明的游标为只读的。UPDATE指定游标中可以更新的列,若有参数OF <列名> [ ,…n ],则只能修改给出的这些列;若在UPDATE中未指出列,则可以修改所有列。
以下是一个符合SQL-92标准的游标声明:
USE PXSCJ
GO
DECLARE XS_CUR1 CURSOR
FOR
SELECT XH, XM, XB, CSSJ, ZXF
FROM XSB
WHERE ZY= '计算机'
FOR READ ONLY
(2)T-SQL扩展。语法格式:
DECLARE <游标名> CURSOR
[ LOCAL | GLOBAL ] /*游标作用域*/
[ FORWORD_ONLY | SCROLL ] /*游标移动方向*/
[ STATIC | KEYSET | DYNAMIC | FAST_FORWARD ] /*游标类型*/
[ READ_ONLY | SCROLL_LOCKS | OPTIMISTIC ] /*访问属性*/
FOR <列名> /*SELECT查询语句*/
[ FOR UPDATE [ OF <列名> [ ,…n ] ] ] /*可修改的列*/
[;]
说明:
① LOCAL与GLOBAL:说明游标的作用域。LOCAL说明所声明的游标是局部游标,其作用域为创建它的批处理、存储过程或触发器,该游标名称仅在这个作用域内有效。在批处理、存储过程、触发器或存储过程 OUTPUT 参数中,该游标可由局部游标变量引用。当批处理、存储过程、触发器终止时,该游标就自动释放。但如果OUTPUT 参数将游标传递回来,则游标仍可引用。GLOBAL说明所声明的游标是全局游标,它在由连接执行的任何存储过程或批处理中都可以使用,在连接释放时游标自动释放。 ② FORWARD_ONLY和SCROLL:说明游标的移动方向。FORWARD_ONLY表示游标只能从第一行滚动到最后一行,即该游标只能支持FETCH的NEXT提取选项。SCROLL含义与SQL-92标准中相同。
③ STATIC|KEYSET|DYNAMIC|FAST_FORWARD:用于定义游标的类型,T-SQL扩展游标有四种类型。
静态游标。关键字STATIC指定游标为静态游标,它与SQL-92标准的INSENSITIVE关键字功能相同。
动态游标。关键字DYNAMIC指定游标为动态游标。与静态游标不同,动态游标能够反映对结果集中所做的更改。 只进游标。关键字FAST_FORWARD定义一个快速只进游标,它是优化的只进游标。
只进游标只支持游标从头到尾顺序提取数据。
键集驱动游标。关键字KEYSET定义一个键集驱动游标。顾名思义,这种游标是由称为键的列或列的组合控制的。
USE PXSCJ
GO
DECLARE XS_CUR2 CURSOR
DYNAMIC
FOR
SELECT XH, XM, ZXF
FROM XSB
WHERE ZY= '计算机'
FOR UPDATE OF ZXF
声明游标后,要使用游标从中提取数据,就必须先打开游标。在T-SQL中,使用OPEN语句打开游标,
其格式为: OPEN { { [ GLOBAL ] <游标名> } | @<游标变量名> }
GLOBAL说明打开的是全局游标,否则打开局部游标。<游标变量名>是一个变量名,该名称可以引用一个游标。 OPEN语句打开游标,然后通过执行在DECLARE CURSOR语句中指定的T-SQL语句填充游标(即生成与游标相关联的结果集)。
例如,语句: OPEN XS_CUR1 打开游标XS_CUR1
该游标被打开后,就可以提取其中的数据。 如果所打开的是静态游标(使用INSENSITIVE或STATIC关键字),那么OPEN将创建一个临时表以保存结果集;如果所打开的是键集驱动游标(使用KEYSET关键字),那么OPEN将创建一个临时表以保存键集。临时表都存储在tempdb数据库中。
【强化练习】定义游标XS_CUR3,然后打开该游标,输出其行数。
DECLARE XS_CUR3 CURSOR
LOCAL SCROLL SCROLL_LOCKS
FOR
SELECT XH, XM, ZXF
FROM XSB
FOR UPDATE OF ZXF
OPEN XS_CUR3
SELECT '游标XS_CUR3数据行数' = @@CURSOR_ROWS 执行结果如下所示:
游标打开后,就可以使用FETCH语句从中读取数据。语法格式如下:
FETCH
[ [ NEXT | PRIOR | FIRST | LAST | ABSOLUTE { n | @nvar } | RELATIVE { n | @nvar} ]
FROM ]
{ { [ GLOBAL ] <游标名> } | @<游标变量名> }
[ INTO @<变量名> [ ,…n ] ]
说明:
NEXT | PRIOR | FIRST | LAST:用于说明读取数据的位置。NEXT说明读取当前行的下一行,并且使其置为当前行。如果FETCH NEXT是对游标的第一次提取操作,则读取的是结果集第一行。NEXT为默认的游标提取选项。PRIOR说明读取当前行的前一行,并且使其置为当前行。如果FETCH PRIOR是对游标的第一次提取操作,则无值返回且游标置于第一行之前。FIRST读取游标中的第一行并将其作为当前行。LAST读取游标中的最后一行并将其作为当前行。FIRST和LAST不能在只进游标中使用。
ABSOLUTE { n | @nvar }和RALATIVE { n | @nvar }:给出读取数据的位置与游标头或当前位置的关系,其中n必须为整型常量,变量@nvar必须为smallint、tinyint或int类型。
INTO:将读取的游标数据存放到指定的变量中。
【强化练习】 (1)打开游标XS_CUR1并从中提取数据。
OPEN XS_CUR1
FETCH NEXT FROM XS_CUR1
(2)从游标XS_CUR2中提取数据。 OPEN XS_CUR2 FETCH FIRST FROM XS_CUR2
FETCH NEXT FROM XS_CUR2 读取下1行(当前行为第2行),
FETCH PRIOR FROM XS_CUR2 读取上1行(当前行为第1行),
FETCH LAST FROM XS_CUR2 读取最后1行(当前行为最后1行),
FETCH RELATIVE -2 FROM XS_CUR2 读取当前行之前的第2行(当前行为倒数第3行),
游标使用完以后,要及时关闭。关闭游标使用CLOSE语句,语法格式如下:
CLOSE { { [ GLOBAL ] <游标名> } | @<游标变量名> }
语句参数的含义与OPEN语句中相同。
例如: CLOSE XS_CUR2 将关闭游标XS_CUR2。
游标关闭后,其定义仍在,需要时可用OPEN语句打开它再使用。若确认游标不再需要,就要释放其定义占用的系统空间,即删除游标。删除游标使用DEALLOCATE语句,
语法格式如下: DEALLOCATE { { [ GLOBAL ] <游标名> } | @<游标变量名> }
语句参数的含义与OPEN和CLOSE语句中相同。
例如: DEALLOCATE XS_CUR2 将删除游标XS_CUR2。