1
数据库原理与应用技术
1.10.1.4 9.1.4 流控制命令
9.1.4 流控制命令

流程控制语句是指用于控制程序执行和流程分支的语句,在SQL Server 2012中,流程控制语句主要用于控制SQL语句、语句块或存储过程的执行流程。

1. BEGIN…END

BEGIN…END包括一系列T-SQL语句,BEGIN…END语句块允许嵌套。

语法格式如下:

BEGIN

{

sql_statement | statement_block

}

END

{ sql_statement | statement_block }是任何有效的T-SQL语句或以语句块定义的语句分组。

虽然所有的T-SQL语句在BEGIN…END块内都有效,但有些T-SQL语句不应分组在同一批处理或语句块中。

2. IF…ELSE

IF…ELSE为指定T-SQL语句的执行条件。如果满足条件,则在IF关键字及其条件之后执行T-SQL语句:布尔表达式返回TRUE。在ELSE关键字引入另一个T-SQL语句,当不满足IF条件时就执行该语句:布尔表达式返回FALSE。

语法格式如下:

IF Boolean_expression

{ sql_statement | statement_block }

[ ELSE

{ sql_statement | statement_block } ]

其中,参数Boolean_expression返回TRUE或FALSE的表达式。如果布尔表达式中含有SELECT语句,则必须用括号将SELECT语句括起来。{ sql_statement | statement_block }表示任何T-SQL语句或用语句块定义的语句分组。除非使用语句块,否则IF或ELSE条件只能执行其后的一条T-SQL语句。

若要定义语句块,必须使用控制流关键字BEGIN和END。

【例9-4】判断一个数是正数还是负数,并编写语句。

解:语句如下。

DECLARE @a int

SET @a=10

IF @a>0

PRINT 'a为正数'

ELSE

PRINT 'a为负数'

输出结果如图9-4所示。

图9-4 例9-4图

我们经常利用IF语句和EXISTS或NOT EXISTS关键字来判断SELECT查询结果是否有记录。

【例9-5】判断c1号课程是否有人选修,并编写语句。

解:语句如下。

USE students

GO

IF EXISTS(SELECT * FROM enroll WHERE cno='c1')

PRINT 'c1号课程已有人选'

ELSE

PRINT 'c1号课程还没有人选'

输出结果如图9-5所示。

图9-5 例9-5图

3. CASE

CASE用于计算条件列表并返回多种可能结果的表达式。CASE表达式有两种格式:

(1) CASE简单表达式,通过将表达式与一组简单的表达式进行比较来确定结果。

(2) CASE搜索表达式,通过计算一组布尔表达式来确定结果。

这两种格式都支持可选的ELSE参数。CASE可允许使用有效表达式的任意语句或子句。例如,可以在SELECT、UPDATE、DELETE和SET等语句及select_list、IN、WHERE、ORDER BY和HAVING等子句中使用CASE。

语法格式为:

CASE input_expression

WHEN when_expression THEN result_expression [ …n ]

[ ELSE else_result_expression ]

END

或者

CASE

WHEN Boolean_expression THEN result_expression [ …n ]

[ ELSE else_result_expression ]

END

【例9-6】根据查询出的成绩确定学生成绩的等级,输入的成绩应在0到100之间,否则就会提示“成绩输入错误,成绩应在0到100之间”(假设现在要查询95001号学生胡峰的c1课程的成绩等级)。试编写语句。

解:语句如下。

USE students

GO

DECLARE @cj float,@str varchar(30)

SELECT @cj=grade FROM enroll WHERE cno='c1' AND sno='95001'

SET @str=

CASE

WHEN @cj>100 OR @cj<0 THEN '成绩输入错误,成绩应在0到100之间'

WHEN @cj>=60 AND @cj<70 THEN '及格'

WHEN @cj>=70 AND @cj<80 THEN '中等'

WHEN @cj>=80 AND @cj<90 THEN '优良'

WHEN @cj>=90 AND @cj<=100 THEN '优秀'

ELSE '不及格'

END

PRINT '该学生的成绩的等级是:'+@str

GO

输出结果如图9-6所示。

图9-6 例9-6图1

在SELECT语句中,CASE 简单表达式只能用于等同性检查,而不能进行其他比较。例题9-6还可以实现如下:

USE students

GO

DECLARE @cj float,@str varchar(30)

SET @str=''

SELECT sno AS 学号,cno AS 课程号,

成绩等级=CASE

WHEN grade>100 OR grade<0 THEN '成绩输入错误,成绩应在0到100之间'

WHEN grade>=60 AND grade<70 THEN '及格'

WHEN grade>=70 AND grade<80 THEN '中等'

WHEN grade>=80 AND grade<90 THEN '优良'

WHEN grade>=90 AND grade<=100 THEN '优秀'

ELSE '不及格'

END

FROM enroll WHERE cno='c1' AND sno='95001'

GO

输出结果如图9-7所示。

图9-7 例9-6图2

4. WHILE

WHILE用于设置重复执行SQL语句或语句块的条件。只要指定的条件为真,就重复执行语句。可以使用BREAK和CONTINUE关键字在循环内部控制WHILE循环中语句的执行。

语法格式为:

WHILE Boolean_expression

{ sql_statement | statement_block | BREAK | CONTINUE }

其中,Boolean_expression表示返回TRUE或FALSE的表达式。如果布尔表达式中含有SELECT语句,则必须用括号将SELECT语句括起来。{sql_statement | statement_block}表示T-SQL语句或用语句块定义的语句分组。若要定义语句块,则使用控制流关键字BEGIN和END。BREAK表示从最内层的WHILE循环中退出。将执行出现在END关键字(循环结束的标记)后面的任何语句。CONTINUE表示使WHILE循环重新开始执行,忽略CONTINUE关键字后面的任何语句。

【例9-7】声明变量数据类型并赋值,用WHILE语句进行判断,若符合条件,则重新循环或退出循环,并编写语句。

解:语句如下。

DECLARE @i int

SET @i=1

WHILE @i<=10 /*循环条件*/

BEGIN

SET @i=@i+1

IF @i=5

BREAK /*无条件退出循环*/

ELSE

CONTINUE /*重新循环*/

END

SELECT @i AS '总计' /*输出结果*/

GO

输出结果如图9-8所示。

图9-8 例9-7图

5. WAITFOR

WAITFOR为延迟语句,指在达到指定时间或时间间隔之前,或者指定语句至少修改或返回一行之前,阻止执行批处理、存储过程或事务。

语法格式为:

WAITFOR

{

DELAY 'time_to_pass'

| TIME 'time_to_execute'

| [ ( receive_statement ) | ( get_conversation_group_statement ) ]

[ , TIMEOUT timeout ]

}

其中,DELAY表示可以继续执行批处理、存储过程或事务之前必须经过的指定时段,最长可为24小时。'time_to_pass'为等待的时段。可以使用datetime数据可接受的格式之一指定 time_to_pass,也可以将其指定为局部变量,但不能指定日期。因此,不允许指定datetime 值的日期部分。TIME 为指定的运行批处理、存储过程或事务的时间。'time_to_execute'为WAITFOR语句完成的时间。receive_statement为有效的RECEIVE语句。get_conversation_ group_statement 为有效的 GET CONVERSATION GROUP 语句。TIMEOUT timeout为指定消息到达队列前等待的时间(以毫秒为单位)。

【例9-8】用TIME关键字指定在下午两点半以后对指定数据表进行查询,并编写语句。

解:语句如下:

SELECT * FROM student

Go

WAITFOR time '14:30:00'

SELECT * FROM student

【例9-9】在WAITFOR语句中使用DELAY参数设置查询语句执行前需要等待的时间间隔,并编写语句。

解:语句如下。

SELECT * FROM student

GO

WAITFOR DELAY '00:00:03'

SELECT * FROM Student

6. RETURN语句

RETURN语句可从查询或过程中无条件退出。RETURN的执行是即时且完全的,可在任何时候用于从过程、批处理或语句块中退出。RETURN之后的语句是不执行的。如果用于存储过程,则RETURN不能返回空值。其语法格式为

RETURN [ integer_expression ]

7. TRY…CATCH

为了增强程序的健壮性,必须对程序中可能出现的错误进行及时处理。在T-SQL语言中,可以使用两种方式处理发生的错误,即使用TRY…CATCH语句(异常处理语句)和使用@@ERROR函数。

TRY…CATCH语句用于实现类似于C#和C++语言中的异常处理的错误处理。T-SQL语句组可以包含在TRY块中。如果TRY块内部发生错误,则会将控制传递给CATCH块中包含的另一个语句组。

TRY…CATCH语句的语法格式如下:

BEGIN TRY

{ sql_statement | statement_block }

END TRY

BEGIN CATCH

{ sql_statement | statement_block }

END CATCH

[ ; ]

其中:sql_statement表示为任何T-SQL语句。statement_block表示为批处理或包含于BEGIN…END块中的任何T-SQL语句组。

注意:① TRY块后必须紧跟相关联的CATCH块。在END TRY和BEGIN CATCH语句之间放置任何其他语句都将生成错误语法。② TRY…CATCH构造既不能跨越多个批处理,也不能跨越多个T-SQL语句块。

在CATCH块的作用域内,可使用以下系统函数来获取错误消息。

(1) ERROR_NUMBER():返回错误号。

(2) ERROR_SEVERITY():返回严重性。

(3) ERROR_STATE():返回错误状态号。

(4) ERROR_PROCEDURE():返回出现错误的存储过程或触发器的名称。

(5) ERROR_LINE():返回导致错误的例程中的行号。

(6) ERROR_MESSAGE():返回错误消息的完整文本

【例9-10】在TRY…CATCH语句中实现对除0错误的捕捉,并编写语句。

解:语句如下。

BEGIN TRY

SELECT 1/0; --产生除0错误

END TRY

BEGIN CATCH

SELECT

ERROR_NUMBER() AS ErrorNumber,

ERROR_SEVERITY() AS ErrorSeverity,

ERROR_PROCEDURE() AS ErrorProcedure

END CATCH

GO

执行结果如图9-9所示。

图9-9 例9-10图

另外,有时会遇到SQL Server实际并不知道的一些错误,但应用系统希望它知道。例如,不希望返回-100;相反,希望能在客户端产生运行错误,而客户端使用时能够唤醒异常处理并进行相应的处理。要完成这一点,就需要在T-SQL中使用RAISERROR语句。语法格式为:

RAISERROR(<message ID | message string>,<severity>,<state>[,<argument> [,<…n>]])

[WITH option[,…n]]

RAISERROR中的参数错误等级信息如表9-5所示。

表9-5 错误信息等级表