T-SQL 流程控制语句
Transact-SQL 语言提供了一些可以用于改变语句执行顺序的命令,称为流程控制语句。流程控制语句允许用户更好地组织存储过程中的语句,方便地实现程序的功能。流程控制语句与常见的程序设计语言类似,主要包含以下几种。
T-SQL 的流程控制语句:
IF…ELSE
BEGIN…END
CASE
WHILE…CONTINUE…BREAK
WAITFOR
GOTO
RETURN
IFELSE 语句:
指定 Transact-SQL 语句的执行条件。如果满足条件,则在 IF 关键字及其条件之后执行 Transact-SQL语句:布尔表达式返回 TRUE。可选的 ELSE 关键字引入另一个Transact-SQL 语句,当不满足 IF 条件时就执行该语句:布尔表达式返回 FALSE。IF...ELSE 构造可用于批处理、存储过程和即时查询。当此构造用于存储过程时,通常用于测试某个参数是否存在。
IFELSE 语句:
IF Boolean_expression
{ sql_statement |statement_block }
[ ELSE
{ sql_statement |statement_block } ]
IF<< span="">条件表达式>
<< span="">命令行或程序块>
[ELSE[条件表达式]
<< span="">命令行或程序块>]
其中<< span="">条件表达式>可以是各种表达式的组合,但表达式的值必须是“真”或“假”。ELSE
子句是可选的。IFELSE 语句用来判断当某一条件成立时执行某段程序,条件不成立时执行另一段程序。如果不使用程序块,IF 或ELSE 只能执行一条命令。IFELSE 可以嵌套使用,最多可嵌套32 级
参数:
Boolean_expression: 返回 TRUE 或 FALSE 的表达式。如果布尔表达式中含有SELECT 语句,则必须用括号将SELECT 语句括起来。
{sql_statement | statement_block }:任何 Transact-SQL 语句或用语句块定义的语句分组。除非使用语句块,否则IF 或 ELSE 条件只能影响一个 Transact-SQL 语句的性能。若要定义语句块,请使用控制流关键字BEGIN 和 END
可以在其他 IF 之后或在 ELSE 下面,嵌套另一个 IF 测试。嵌套级数的限制取决于可用内存
示例:在 uspGetList 存储过程的输出中使用了IFELSE。该存储过程在创建存储过程中定义。在此示例中,该存储过程返回标价低于700 美元的自行车清单。这将导致执行第一个 PRINT 语句
DECLARE @compareprice money,@cost money
EXECUTEProduction.uspGetList ‘%Bikes%‘, 700,
@compareprice OUT,
@cost OUTPUT
IF @cost <= @compareprice
BEGIN
PRINT ‘These products can bepurchased for less than
$‘+RTRIM(CAST(@comparepriceAS varchar(20)))+‘.‘
END
ELSE
PRINT ‘The prices for allproducts in this category exceed
$‘+ RTRIM(CAST(@comparepriceAS varchar(20)))+‘.‘
创建一个随机数/2,看是否余数为0,若为0,返回a,若不为0,返回b
declare @num int
set @num =(convert(int,RANd (11)*100000))
if @num % 2=0
print ‘a‘
else
print ‘b‘
BEGIN…END 语句
BEGIN 和 END 语句用于将多个Transact-SQL 语句组合为一个逻辑块。在控制流
语句必须执行包含两条或多条Transact-SQL 语句的语句块的任何地方,都可以使用
BEGIN 和 END 语句。
EGIN 和 END 语句用于下列情况:
WHILE 循环需要包含语句块。
CASE 函数的元素需要包含语句块。
IF 或 ELSE 子句需要包含语句块。
语法:
BEGIN
{
sql_statement |statement_block
}
END
参数:{ sql_statement | statement_block }使用语句块定义的任何有效的Transact-SQL
语句或语句组
示例:
A、在下面的示例中,BEGIN 和 END 定义一系列一起执行的Transact-SQL语句。如
果不包括 BEGIN...END 块,则将执行两个ROLLBACKTRANSACTION 语句,并返回
两条 PRINT 消息
USE AdventureWorks;
GO
BEGIN TRANSACTION;
GO
IF @@TRANCOUNT = 0
BEGIN
SELECT *
from Person.Contact
WHERE LastName = ‘ADAMS‘;
ROLLBACK TRANSACTION
PRINT N‘Rolling back thetransaction two times would cause an error.‘
END
ROLLBACK TRANSACTION
PRINT N‘Rolled back thetransaction.‘
GO
/*
Rolled back the tranaction.
*/
B、创建一个测试表,并循环写入500 条数据,使用newid():
declare @num int =1
while @num <=< span="">500
begin
print ‘test‘
set @num=@num+1
end
CASE
计算条件列表并返回多个可能结果表达式之一。
CASE 具有两种格式:
简单CASE 函数将某个表达式与一组简单表达式进行比较以确定结果。
CASE 搜索函数计算一组布尔表达式以确定结果。
CASE 语法:
Simple CASEfunction:
CASE input_expression
WHEN when_expression THENresult_expression
[ ...n ]
[
ELSE else_result_expression
]
END
SearchedCASE function:
CASE
WHEN Boolean_expression THENresult_expression
[ ...n ]
[
ELSE else_result_expression
]
END
参数:
input_expression:使用简单CASE 格式时所计算的表达式。input_expression 是任意
有效的表达式。
WHENwhen_expression:使用简单CASE 格式时要与input_expression 进行比较的简单表达式。when_expression是任意有效的表达式。input_expression及每个when_expression 的数据类型必须相同或必须是隐式转换的数据类型。
n :占位符,表明可以使用多个 WHEN when_expressionTHEN result_expression 子句或多个 WHEN Boolean_expression THEN result_expression 子句。
THENresult_expression : 当 input_expression = when_expression 计算结果为TRUE, 或者 Boolean_expression 计算结果为TRUE 时返回的表达式。Result expression是任意有效的表达式。
ELSEelse_result_expression:比较运算计算结果不为TRUE 时返回的表达式。如果忽略此参数且比较运算计算结果不为TRUE , 则CASE 返回 NULL 。
else_result_expression 是任意有效的表达式。else_result_expression 及任何
result_expression 的数据类型必须相同或必须是隐式转换的数据类型。
WHENBoolean_expression : 使用 CASE 搜索格式时所计算的布尔表达式。
Boolean_expression 是任意有效的布尔表达式。
结果值:
简单CASE 函数:
l 计算input_expression, 然后按指定顺序对每个WHEN 子句的
input_expression =when_expression 进行计算。
l 返回 input_expression = when_expression 的第一个计算结果为TRUE 的
result_expression。
l 如果 input_expression = when_expression 的计算结果均不为TRUE,则在指定了 ELSE 子句的情况下, SQL Server 数据库引擎将返回
else_result_expression;若没有指定 ELSE 子句,则返回NULL 值。
CASE 搜索函数:
l 按指定顺序对每个WHEN 子句的 Boolean_expression 进行计算。
l 返回 Boolean_expression 的第一个计算结果为TRUE 的 result_expression。
l 如果 Boolean_expression 计算结果不为TRUE,则在指定ELSE 子句的情况下数据库引擎将返回else_result_expression;若没有指定 ELSE 子句,则返回NULL 值。
示例:
A. 使用带有简单CASE 函数的 SELECT 语句
在 SELECT 语句中,简单CASE 函数仅检查是否相等,而不进行其他比较。以下示例使用 CASE 函数更改产品系列类别的显示,以使这些类别更易理解。
USE AdventureWorks;
GO
SELECT ProductNumber,Category =
CASE ProductLine
WHEN ‘R‘ THEN ‘Road‘
WHEN ‘M‘ THEN ‘Mountain‘
WHEN ‘T‘ THEN ‘Touring‘
WHEN ‘S‘ THEN ‘Other saleitems‘
ELSE ‘Not for sale‘
END,
Name
FROM Production.Product
ORDER BY ProductNumber;
GO
B、使用带有CASE 搜索函数的 SELECT 语句
在 SELECT 语句中,CASE 搜索函数允许根据比较值在结果集内对值进行替换。下面的示例根据产品的价格范围将标价显示为文本注释。
USE AdventureWorks;
GO
SELECT ProductNumber, Name,‘Price Range‘ =
CASE
WHEN ListPrice = 0 THEN ‘Mfgitem - not for resale‘
WHEN ListPrice < 50 THEN‘Under $50‘
WHEN ListPrice >= 50 andListPrice < 250 THEN ‘Under $250‘
WHEN ListPrice >= 250 andListPrice < 1000 THEN ‘Under $1000‘
ELSE ‘Over $1000‘
END
FROM Production.Product
ORDER BY ProductNumber ;
GO
WHILE…CONTINUE…BREAK
只要指定的条件为True 时,WHILE 语句就会重复语句或语句块。
下面两个 Transact-SQL 语句通常和WHILE 一起使用:REAK 或 CONTINUE。
BREAK 语句退出最内层的WHILE 循环,CONTINUE 语句则重新开始WHILE 循环。例如,如果没有其他行可以处理,程序可能执行BREAK 语句。例如,如果要继续执行代码,则可以执行CONTINUE 语句。
如果将 SELECT 语句用作WHILE 语句的条件,则SELECT 语句必须在括号中
语句:
WHILE << span="">条件表达式>
BEGIN
<< span="">命令行或程序块>
[BREAK]
[CONTINUE]
[命令行或程序块]
END
WHILE 命令在设定的条件成立时会重复执行命令行或程序块。
CONTINUE 命令可以让程序跳过CONTINUE 命令之后的语句,回到WHILE 循环的第一行命令。
BREAK 命令则让程序完全跳出循环,结束WHILE 命令的执行。
WHILE 语句也可以嵌套。
示例:
A、在游标中使用WHILE
下面的示例使用 WHILE 语句控制执行的提取数。
USE AdventureWorks;
GO
DECLARE abc CURSOR FOR
SELECT * FROMPurchasing.ShipMethod;
OPEN abc;
FETCH NEXT FROM abc
WHILE (@@FETCH_STATUS = 0)
FETCH NEXT FROM abc;
CLOSE abc;
DEALLOCATE abc;
GO
B. 在嵌套的 IF...ELSE 和 WHILE 中使用 BREAK 和 CONTINUE
在以下示例中,如果产品的平均标价小于$300,则WHILE 循环将价格乘2,然后选择最高价格。如果最高价格小于或等于$500,则WHILE 循环重新开始,并再次将价格乘 2。该循环不断地将价格乘2,直到最高价格超过$500,然后退出 WHILE 循环,并输出一条消息。
USE AdventureWorks;
GO
WHILE (SELECT AVG(ListPrice)FROM Production.Product) < $300
BEGIN
UPDATE Production.Product
SET ListPrice = ListPrice *2
SELECT MAX(ListPrice) FROMProduction.Product
IF (SELECT MAX(ListPrice)FROM Production.Product) > $500
BREAK
ELSE
CONTINUE
END
PRINT ‘Too much for themarket to bear‘;
C、创建一个数学运算表达式:
DECLARE @x INT, @y INT, @c INT
SELECT @x = 1, @y = 1
WHILE @x < 3
BEGIN
PRINT @x --打印变量x 的值
WHILE @y < 3
BEGIN
SELECT @c = 100*@x+ @y
PRINT @c --打印变量c 的值
SELECT @y = @y + 1
END
SELECT @x = @x + 1
SELECT @y = 1
END
返回结果:
1 101 102
2 201 202
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 语句完成的时间。可以使用datetime数据可接受的格式之一指定 time_to_execute,也可以将其指定为局部变量。不能指
定日期;因此,不允许指定datetime值的日期部分。
receive_statement:有效的 RECEIVE语句。
get_conversation_group_statement:有效的 GETCONVERSATION GROUP 语句
TIMEOUT timeout:指定消息到达队列前等待的时间(以毫秒为单位)。
示例:
A. 使用 WAITFOR TIME
以下示例在晚上 10:20(22:20) 执行存储过程sp_update_job
USEmsdb;
EXECUTEsp_add_job @job_name = ‘TestJob‘;
BEGIN
WAITFORTIME ‘22:20‘;
EXECUTEsp_update_job @job_name = ‘TestJob‘,
@new_name= ‘UpdatedJob‘;
END;
GO
B、使用 WAITFOR DELAY
以下示例在两小时的延迟后执行存储过程。
BEGIN
WAITFORDELAY ‘02:00‘;
EXECUTEsp_helpdb;
END;
GO
C、等待1 小时2 分零3 秒后才执行SELECT 语句
WAITFORDELAY 01:02:03
SELECT* FROM Toys
D、等到晚上11 点零8 分后才执行SELECT 语句
WAITFORTIME 23:08:00
SELECT* FROM Toys
GOTO
将执行流更改到标签处。跳过GOTO 后面的 Transact-SQL语句,并从标签位置继续处理。GOTO 语句和标签可在过程、批处理或语句块中的任何位置使用。GOTO 语句
可嵌套使用,
语法:
Definethe label:
label:
Alterthe execution:
GOTOlabel
求例:
分行打印字符1、2、3、4、5。
DECLARE@x INT
SELECT@x = 1
lab_1:PRINT @x
SELECT@x = @x+ 1
WHILE@x < 6
GOTO lab_1
以下示例显示如何将GOTO 用作分支机制
DECLARE@Counter int;
SET @Counter = 1;
WHILE@Counter < 10
BEGIN
SELECT@Counter
SET @Counter = @Counter+ 1
IF @Counter = 4 GOTO Branch_One --Jumps to the first branch.
IF @Counter = 5 GOTO Branch_Two --This will never execute.
END
Branch_One:
SELECT‘Jumping To Branch One.‘
GOTO Branch_Three; --This will prevent Branch_Two from executing.
Branch_Two:
SELECT‘Jumping To Branch Two.‘
Branch_Three:
SELECT‘Jumping To Branch Three.‘
RETURN
RETURN命令用于结束当前程序的执行,返回到上一个调用它的程序或其它程序。
在括号内可指定一个返回值
语法:
RETURN[ integer_expression ]
求例:
A. 从过程返回:
以下示例显示如果在执行findjobs 时没有指定用户名作为参数,则RETURN将使过程
向用户屏幕发送一条消息后退出。如果指定了用户名,则将从相应的系统表中检索此用户
在当前数据库创建的所有对象名。
CREATEPROCEDURE findjobs @nm sysname = NULL
AS
IF @nm IS NULL
BEGIN
PRINT‘You must give a user name‘
RETURN
END
ELSE
BEGIN
SELECTo.name, o.id, o.uid
FROM sysobjects o INNERJOIN master..sysloginsl
ON o.uid = l.sid
WHEREl.name= @nm
END;
B. 返回状态代码
以下示例将检查指定联系人的ID 的状态。如果所在的州是Washington (WA),将返回状态代码1。在其他情况下(StateProvince的值是WA 以外的值,或者ContactID没有匹配的行),返回状态代码2。
USE AdventureWorks;
GO
CREATEPROCEDURE checkstate @param varchar(11)
AS
IF (SELECT StateProvinceFROM Person.vAdditionalContactInfo WHERE ContactID =
@param) = ‘WA‘
RETURN1
ELSE
RETURN2;
GO
T-SQL常用SQL 语句
语句 功能 语句 功能
数据操作
SELECT从数据库表中检索数据DELETE 从数据库表中删除数据行
INSERT向数据库表中添加数据行UPDATE更新数据库表中的数据
数据定义
CREATETABLE 创建一个数据库表CREATE
PROCEDURE
创建一个存储过程
DROPTABLE 从数据库中删除表DROP PROCEDURE 从数据库中删除存储过程
ALTERTABLE 修改数据库表结构CREATE TRIGGER 创建一个触发器
CREATEVIEW 创建一个视图DROPTRIGGER 从数据库中删除触发器
DROPVIEW 从数据库中删除视图CREATEDOMAIN 创建一个数据值域
CREATEINDEX 为数据库表创建一个索引ALTERDOMAIN 改变域定义
DROPINDEX 从数据库中删除索引DROP DOMAIN 从数据库中删除域
数据控制
GRANT授予用户访问权限REVOKE解除用户访问权限
DENY拒绝用户访问
事务控制
COMMIT结束当前事务SAVERANSACTION 在事务内设置保存点
ROLLBACK回滚当前事务
程序化SQL
DECLARE设定游标CLOSE 关闭游标
OPEN打开一个游标PREPARE 为动态执行准备SQL 语句
FETCH检索一行查询结果EXECUTE 动态执行SQL 语句
补充:
SQL 2005 查询过程:
在SQLServer 2005 版本中,将一个联接查询的逻辑处理分为了10 个步骤:
(8) SELECT (9) DISTINCT (11)
(1) FROM
(3) JOIN
(2) ON
(4) WHERE
(5) GROUP BY
(6) WITH {CUBE | ROLLUP}
(7) HAVING
(10) ORDER BY
SQL 2008 查询过程:
在SQLServer 2008 版本中,则对逻辑阶段的描述扩展到了所有的逻辑语句,而不仅仅是联接处理,如APPLY、PIVOT等。按这种分类方式,将逻辑步骤分成了6 部分,部分步骤中包含了子步骤。
(5)SELECT (5-2)DISTINCT (5-3) (5-1)
(1)FROM (1-J) JOIN ON
|(1-A) APPLY AS
|(1-P) PIVOT() AS
|(1-U) UNPIVOT() AS
(2)WHERE
(3)GROUP BY (3-CR)WITH {CUBE | ROLLUP}
(4)HAVING
(6)ORDER BY
各处理步骤流程描述:
l 步骤1(FROM):该步骤中用于验证查询的源表,并处理表操作符。每个表操作符应用于一系列子步骤。例如,在上面用于联接的(1-J)步骤中会涉及如下的子步骤。最终这些子步骤完成后,将生成虚拟表VT1。
(1-J1):执行left_table 和right_table两个表的交叉联接(笛卡儿乘积),生成
虚拟表VT1-J1;
(1-J2):对笛卡儿乘积应用ON 筛选器,生成虚拟表VT1-J2;
(1-J3):如果是外部联接,会在该步骤中将被ON 筛选掉的外部行添加到VT1-J2
中,生成VT1-J3。否则,将跳过该步骤。
l 步骤2(WHERE):对VT1 应用WHERE 筛选器,将符合筛选条件的行插入到VT2 中。
l 步骤3(GROUP BY):按GROUP BY子句中的列列表对VT2中的行分组,生成VT3。
如果语句中包含WITH CUBE 或WITH ROLLUP,则将分组统计结果再次加总后插入VT3,生成VT3-RC。
l 步骤4(HAVING):对VT3 应用HAVING 筛选器,将符合筛选条件的行插入到VT4。
l 步骤5(SELECT):处理SELECT 子句中的元素,生成VT5。
(5-1)计算表达式:该步骤计算SELECT 列表中的表达式,生成VT5-1;
(5-2)DISTINCT:从VT5-1中移除重复行,生成VT5-2;
(5-3)TOP:该步骤根据ORDER BY 子句中指定的排序规则,从VT5-2 的开始
处筛选出指定数量或比例的行。
l 步骤6(ORDER BY):该步骤对VT5-3 中的行按ORDERBY子句中的列列表进行排序,生成一个游标VC6