2-2 T-SQL流程控制语句
u
学习IF
…ELSE
,条件语句
u
学习WHILE
…CONTINUE
…BREAK
,循环语句
u
学习CASE
,多条件分支语句
u
学习GOTO
,跳转语句
u
学习Try
…Catch
,错误与意外处理语句
流程控制语句是指那些用来控制程序执行和流程分支的语句,在
SQL Server 2005
中,流程控制语句主要用来控制
SQL
语句、语句块或者存储过程的执行流程。
2-2-1 IF…ELSE语句
在流程控制语句之中,首先我们应当认识的是语句块,一个语句块是以
BEGIN
开始,以
END
语句作为终止,作为一个完全独立的逻辑单元存在于流程控制语句之中,如下所示:
BEGIN
{ sql_statement | statement_block }
END
而
IF…ELSE
语句是条件判断语句,其中,
ELSE
子句是可选的,最简单的
IF
语句没有
ELSE
子句部分。
IF…ELSE
语句用来判断当某一条件成立时执行某段程序,条件不成立时执行另一段程序。
SQL Server
允许嵌套使用
IF…ELSE
语句,而且嵌套层数没有限制。
IF…ELSE
语句的语法形式为:
IF Boolean_expression
{ sql_statement | statement_block }
[ ELSE
{ sql_statement | statement_block } ]
实验: IF…ELSE
语句实验
--
例1
:如果员工数据表中有人在办公室工作,则显示这些人名单,否则告知没有人在办公室。
Use sample
Go
--
声明用于发布消息的变量
Declare @message varchar(200)
--
判断是否在办公室工作
If exist(Select * from
员工数据表 where
所属部门=’
办公室’)
--
如果有,则列出姓名
Begin
set @message=’
下列人员在办公室
:’
print @message
select
姓名 from
员工数据表 where
所属部门=’
办公室’
End
--
否则,输出没有人的在工作的消息
Else
Begin
Set @message=’
抱歉,没有人在办公室’
Print @message
End
Go
--
注意
:begin
和end
分别表示语句块的开始和结束,而且他们必须成对使用。
--
例子2
:查询如果有工资超过5000
元输出一个消息,否则输出另一个消息
Use sample
Go
Declare @message varchar(200)
--
判断是否存在工资超过5000
的员工
If exist(Select
姓名,
工资 from
员工数据表 where
工资>5000)
Begin
Print ’
有员工工资超过5000
元’
End
--
否则,输出没有工资以上的员工
Else
Begin
Set @message=’
抱歉
,
并没有员工的工资在5000
元以上’
Print message
End
--
例子3
:下面的例子中,如果员工平均薪水大于3000
元,则输出一个消息,否则输出另一个消息
If (select avg(
工资) from
员工数据表)>3000
print ’
我们要求提高工资水平!’
Else
print ’
工资水平较为合理!’
2-2-2 WHILE…CONTINUE…BREAK语句
WHILE…CONTINUE…BREAK
语句用于设置重复执行
SQL
语句或语句块的条件。只要指定的条件为真,就重复执行语句。其中,
CONTINUE
语句可以使程序跳过
CONTINUE
语句后面的语句,回到
WHILE
循环的第一行命令。
BREAK
语句则使程序完全跳出循环,结束
WHILE
语句的执行,其语法形式为:
WHILE Boolean_expression
{ sql_statement | statement_block }
[ BREAK ]
{ sql_statement | statement_block }
[ CONTINUE ]
实验: WHILE…CONTINUE…BREAK
语句实验
--
例1
、下面的这个例子将判断如果员工平均薪水少于3000
元,则循环每次使得每个员工工资增加5
元,直到所有的员工的平均工资都是大于3000
元或者有的员工的工资超过了10000
元为止。
Use sample
Go
--
根据是否存在员工的平均工资少于3000
而确定循环是否继续执行
While (select avg(
工资) from
员工数据表)<3000
--
循环开始
begin
Update
员工数据表
Set
工资=
工资+5
--
如果有的员工的工资已经超过了10000
元,则跳出循环
print '
没有的员工的工资已经超过了10000
元,继续循环'
If (select max(
工资) from
员工数据表)>10000
Break
else
continue
end
--
循环结束
print '
有的员工的工资已经超过了10000
元,停止循环'
--
例2
、另一种解法示例
declare @salary money,@min_money money,@max_money money,@avg_money money
select @min_money=MIN(
工资),@max_money=MAX(
工资),@avg_money=AVG(
工资) FROM
员工数据表
while @avg_money<3000
begin
update
员工数据表
set
工资=
工资+500
if @max_money>10000
break
else
continue
End
--
小问题:案例2
出现死循环了,怎么解决?
问题分析:由于例2
代码的第二行所查询出来的数据是初次数据表的最小值、最大值和平均值,这些数据不会在后面的循环过程的累加变化中而动态变化,即循环条件永远满足,从而导致死循环的产生。
--
例2
、错误更正(1
)
declare @salary money,@min_money money,@max_money money,@avg_money money
--
此处是进行局部变量的初次赋值;
select @min_money=MIN(
工资),@max_money=MAX(
工资),@avg_money=AVG(
工资) FROM
员工数据表
while @avg_money<3000
begin
--
此处赋值是每次循环时候,改变各个变量的值。
select @min_money=MIN(
工资),@max_money=MAX(
工资),@avg_money=AVG(
工资) FROM
员工数据表
update
员工数据表
set
工资=
工资+500
if @max_money>10000
break
else
continue
End
--
小问题:案例2
的错误更正虽然解决了死循环的问题,但是依然是错误的,为什么?
主要原因是在进行update
修改数据后,并没有及时赋值,程序将继续开始循环,但此时while
判断的@avg_money
变量的值已经不是最新的了,从而导致循环次数可能变多一次。
--
例2
、错误更正(2
)
declare @salary money,@min_money money,@max_money money,@avg_money money
--
此处是进行局部变量的初次赋值;
declare @salary money,@min_money money,@max_money money,@avg_money money
select @min_money=MIN(
工资),@max_money=MAX(
工资),@avg_money=AVG(
工资)
FROM
员工数据表
while @avg_money<3000
begin
update
员工数据表 set
工资=
工资+500
if @max_money>10000
break
else
begin
--
调整位置,每次continue
之前先进性赋值。
select @min_money=MIN(
工资),@max_money=MAX(
工资),@avg_money=AVG(
工资)
FROM
员工数据表
continue
end
End
--
例2
、错误更正(3
),本方法比较简洁明了。
while (select AVG(
工资) from
员工数据表)<3000
begin
update
员工数据表 set
工资=
工资+500
if (select max(
工资) from
员工数据表)>10000
break
else
continue
End
2-2-3 CASE语句
CASE
语句可以计算多个条件式,并将其中一个符合条件的结果表达式返回。
CASE
语句按照使用形式的不同,可以分为简单
CASE
语句和搜索
CASE
语句,它们的语法形式分别为:
CASE input_expression
WHEN when_expression THEN result_expression
[ ...n ]
[ELSE else_result_expression ENDCASE
WHEN Boolean_expression THEN result_expression
[...n ]
[ELSE else_result_expression END
实验: CASE
语句实验
--
例1
:根据员工数据表中每个员工所在不同部门,显示不同的信息
Use sample
Go
Select
姓名, ’
部门说明’=
--
分别为各个部门说明情况
Case
所属部门
when ’
办公室’ then ’
在办公室工作,该部门主要负责办公室的工作。’
when ’
项目部’ then ’
在项目部工作,该部门主要负责项目部的工作。’
when ’
录入部’ then ’
在录入部工作,该部门主要负责录入部的工作。’
when ’
技术部’ then ’
在技术部工作,该部门主要负责技术部的工作。’
End
From
员工数据表
--
按照员工的数据表排序
Order by
姓名
2-2-4 waitfor语句
WAITFOR
语句用于暂时停止执行
SQL
语句、语句块或者存储过程等,直到所设定的时间已过或者所设定的时间已到才继续执行。
WAITFOR
语句的语法形式为:
WAITFOR { DELAY 'time' | TIME 'time' }
其中,
DELAY
用于指定时间间隔,
TIME
用于指定某一时刻,其数据类型为
datetime
,格式为
‘hh:mm:ss’
。
实验: waitfor
语句实验
--
例1
:
Use sample
Go
--
指定在执行select
语句之前需要等待3
秒
Waitfor delay ’00:00:03’
--
执行查询
select
姓名,
性别 from
员工数据表 where
所属部门=’
项目部’
2-2-5 GOTO语句
GOTO
语句可以使程序直接跳到指定的标有标识符的位置处继续执行,而位于
GOTO
语句和标识符之间的程序将不会被执行。
GOTO
语句和标识符可以用在语句块、批处理和存储过程中,标识符可以为数字与字符的组合,但必须以
“: ”
结尾。如:
‘a1: ’
。在
GOTO
语句行,标识符后面不用跟
“: ”
。
GOTO
语句的语法形式为:
GOTO label
(标签名称)
……
label:
实验: GOTO
语句实验
--
例1
:利用GOTO
语句求出从1
加到5
的总和。程序清单如下:
declare
@sum int, @count int
select
@sum=0, @count=1
label_1:
select
@sum=@sum+@count
select
@count=@count+1
if
@count<=5
goto
label_1
select @count
@sum
--
注意:
人们认为GOTO
语句是影响可读性的严重因素,在使用的时候尽可能避免使用GOTO
语句,因为过多的GOTO
语句可能会造成T-SQL
的逻辑混乱而难以理解。另外,标签仅仅标示了跳转的目标,它并不隔离其前后的语句。只要标签前面的语句本身不是流程控制语句,标签前后的语句将按照顺序正常执行,就如同没有使用标签一样。
2-2-6 错误处理与Try …Catch语句
这是
MSSQL2005
特有的一种标准的捕获和处理错误的方法,其基本理念是,尝试执行一个代码块,如果发生错误,则在
Catch
代码块之中捕获错误,其基本用法如下:
BEGIN TRY
{ sql_statement | statement_block } ;
END TRY
BEGIN CATCH
{ sql_statement | statement_block }
END CATCH
和普通语言的异常处理用法差不多,但要注意的是,
SQL SERVER
只捕捉那些不是严重的异常,当比如数据库不能连接等这类异常时,是不能捕捉的。在捕获异常错误时候,经常会使用到一些系统的错误函数,具体内容见表
2-3
所示。
表2-3
异常出错时候使用的捕获错误的系统函数
错误函数
|
返回值
|
Error_Message()
|
错误的消息文本
|
Error_Number()
|
错误编号
|
Error_Procedure()
|
发生错误的存储过程或触发器的名称
|
Error_Serverity()
|
错误的严重程度
|
Error_State()
|
错误的状态
|
在进行异常捕获时候,对于
CATCH
块我们需要注意处理好下面的工作:
(1)
如果批处理使用了逻辑结构(
begin tran/commit tran
),则错误处理程序应回滚事务。建议首先会滚事务,以释放该事务执行的锁定。
(2)
如果错误是存储过程逻辑检测到的,则系统将自动引发错误消息。
(3)
如果有必要,将错误记录到错误表中。
(4)
结束批处理,如果它是存储过程、用户定义函数或触发器,可使用
return
命令结束它。
实验: GOTO
语句实验
--
例1
:除0
错误时候,捕获异常错误:
BEGIN TRY
DECLARE @X INT
-- 0
作为除数错误
SET @X = 1/0
PRINT 'TRY
模块运行正常'
END TRY
BEGIN CATCH
PRINT '
出现异常错误'
SELECT ERROR_NUMBER() ERNumber,
ERROR_SEVERITY() Error_Severity,
ERROR_STATE() Error_State,
ERROR_PROCEDURE() Error_Procedure,
ERROR_LINE() Error_Line,
ERROR_MESSAGE() Error_Message
END CATCH
PRINT 'TRY/CATCH
执行完毕后显示信息'
--
例2
、try
…catch
可以嵌套的案例
Use school
GO
Begin TRY
delete from student where sName = '
小明'
print '
小明同学已经被成功删除。'
End Try
Begin Catch
Print '
删除信息时候有错误发生'
Begin Try
delete from score where sno = (select distinct sno from student where sName = '
小明')
Print '
小明同学第二次被成功删除。'
End Try
Begin Catch
print '
删除信息第二次错误发生'
Begin Try
delete from student where sno =
(select distinct sno from score where cno = (select distinct cno from course where cName = 'c
语言'))
print '
第三次删除学生成功。'
End Try
Begin Catch
Print '
删除信息第三次错误发生'
End Catch
End Catch
End Catch