第 9 章  Transact-SQL 程序设计
本章内容 9.1  数据与表达式 9.2  函  数 9.3  程序控制流语句 9.4  游标管理与应用
9.1  数据与表达式 9.1.1  用户定义数据类型 9.1.2  常量与变量 9.1.3  运算符与表达式
9.1.1  用户定义数据类型 1 .使用系统存储过程来创建用户定义数据类型 , 命令格式如下: sp_addtype [@typename=] type, [@phystype = ] system_data_type [, [ @nulltype = ] 'null_type' ] [, [ @owner = ] 'owner_name' ] 9.1  数据与表达式
例如,为 Sales 数据库创建—个不允许为 NULL 值的 test_add 用户定义数据类型。 USE Sales GO EXEC sp_addtype test_add,'Varchar(10)','NOT NULL' GO 此后, test_add 可用为数据列或变量的数据类型。 9.1.1  用户定义数据类型
2 .使用企业管理器创建用户定义数据类型 在企业管理器中,为 Sales 数据库创建—个不允许 NULL 值的 test_add 用户定义数据类型,操作步骤如下。 (1) 选择 Sales 数据库。 (2) 在左窗格中选择“用户定义的数据类型”项,单击鼠标右键,在出现的快捷菜单中选择“新建用户定义数据类型”命令。 (3) 在“用户定义的数据类型属性”对话框中的文本框内输入 test_add 。 (4) 在“数据类型”下拉列表框中,选择 char 。 (5) 在“长度”文本框中输入 10 。 (6) 选中“允许 NULL 值”复选框。 (7) 单击“确定”按钮完成创建用户自定义数据类型。 9.1.1  用户定义数据类型
9.1.2  常量与变量 在程序运行中保持常值的数据,即程序本身不能改变其值的数据,称为常量,在程序中经常直接使用文字符号表示。 相应地,在程序运行过程中可以改变其值的数据,称为变量。  9.1  数据与表达式
1 .常量 常量是表示特定数据值的符号,其格式取决于其数据类型 (1) 字符串和二进制常量 字符串常量括在单引号内并包含字母数字字符 (a-z 、 A-Z  和  0-9) 以及特殊字符,如感叹号 (!) 、 at  符 (@) 和数字号 (#) 。 例如:‘ Cincinnati’ 、 ‘ Process X is 50% complete.’ 为字符串常量。 二进制常量具有前辍 0x 并且是十六进制数字字符串,它们不使用引号。例如 0xAE 、 0x12Ef 、 0x69048AEFDD010E 、 0x( 空串 ) 为二进制常量。 9.1.2  常量与变量
(2) 日期 / 时间常量 datetime 常量使用特定格式的字符日期值表示,用单引号括起来。 输入时,可以使用“ /” 、“ .” 、“ -” 作日期 / 时间常量的分隔符。  9.1.2  常量与变量 2005-09-03 00:00:00 2005-09-03 00:00:00.000 9/3/2005 1900-01-01 13:25:00 1900-01-01 13:25:19.000 13:25:19 2005-09-03 13:00:00 2005-09-03 13:00:00.000 9.3.2005 13:00 2005-09-03 13:00:00 2005-09-03 13:00:00.000 9/3/2005 1PM 2005-09-03 01:35:00 2005-09-03 01:34:34.123 Sep 3, 2005 1:34:34.122 Smalldatetime 值 datetime 值 输入格式
(3) 数值常量 ① 整型常量 --- 由没有用引号括起来且不含小数点的一串数字表示。例如, 1894 、 2 为整型常量。 ② 浮点常量 --- 采用科学记数法表示,例如, 101.5E5 、 0.5E-2 为浮点常量。 ③ 精确数值常量 --- 由没有用引号括起来且包含小数点的一串数字表示。例如, 1894.1204 、 2.0 为精确数值常量。 ④ 货币常量 --- 以“ $” 为前缀的一个整型或实型常量数据,不使用引号。例如, $12.5 、 $542023.14 为货币常量。 ⑤ uniqueidentifier 常量 --- 表示全局惟一标识符 GUID 值的字符串。可以使用字符或二进制字符串格式指定。 9.1.2  常量与变量
(4) 逻辑数据常量 逻辑数据常量使用数字 0 或 1 表示,并且不使用引号。非 0 的数字当作 1 处理。 (5) 空值 在数据列定义之后,还需确定该列是否允许空值 (NULL) 。允许空值意味着用户在向表中插入数据时可以忽略该列值。空值可以表示整型、实型、字符型数据。 9.1.2  常量与变量
变量用于临时存放数据,变量中的数据随着程序的运行而变化,变量有名字与数据类型两个属性。 变量的命名使用常规标识符,即以字母、下划线 (_) 、 at 符号 (@) 、数字符号 (#) 开头,后续字母、数字、 at 符号、美元符号 ($) 、下划线的字符序列。不允许嵌入空格或其他特殊字符。 2 .变量 9.1.2  常量与变量
全局变量由系统定义并维护,通过在名称前面加“ @@” 符号 局部变量的首字母为单个“ @” 。 全局变量和局部变量
(1) 局部变量 局部变量使用 DECLARE 语句定义 DECLARE {@local_variable data_type }[,...n] 变量名最大长度为 30 个字符。一条 DECLARE 语句可以定义多个变量,各变量之间使用逗号隔开。 例如 DECLARE @name varchar(30),@type int 9.1.2  常量与变量
局部变量的赋值 局部变量用 select 、 print 显示,用 select 、 update 、 set 赋值 ① 用 SELECT 为局部变量赋值 SELECT @variable_name=expression[ , … n] FROM  … WHERE  … 例如 DECLARE @int_var int SELECT @int_var =12  /* 给 @int_var 赋值 */ SELECT @int_var  /* 将 @int_var 的值输出到屏幕上 */
在一条语句中可以同时对几个变量进行赋值 例如 DECLARE @LastName char(8),@Firstname char(8),@BirthDate datetime SELECT @LastName='Smith',@Firstname='David',@BirthDate='1985-2-20' SELECT @LastName,@Firstname,@BirthDate 局部变量没有被赋值前,其值是 NULL ,若要在程序中引用它,必须先赋值。  9.1.2  常量与变量
例 9-1  使用 SELECT 语句从 customer 表中检索出顾客编号为“ C0002” 的行,再将顾客的名字赋给变量 @customer 。 DECLARE @customer varchar(40),@curdate datetime SELECT @customer=customer_name,@curdate=getdate() FROM customer WHERE customer_id='C0002' 9.1.2  常量与变量
② 利用 UPDATE 为局部变量赋值 例 9-2  将 sell_order 表中的 transporter_id 列值为“ T001” 、 goods_id 列值为“ G00003” 的 order_num 列的值赋给局部变量 @order_num 。 DECLARE @order_num float UPDATE sell_order SET @order_num=order_num*2 WHERE transporter_id='T001' AND goods_id='G00003' 9.1.2  常量与变量
③  用 SET 给局部变量赋值 SET 语句格式为: SET {@local_variable=expression} 使用 SET 初始化变量的方法与 SELECT 语句相同, 但一个 SET 语句只能为一个变量赋值 。 例 9-3  计算 employee 表的记录数并赋值给局部变量 @rows 。 DECLARE @rows int SET @rows=(SELECT COUNT(*) FROM employee) SELECT @rows 9.1.2  常量与变量
例  声明一个 datetime 类型的局部变量。 DECLARE @date_var datetime 例  声明两个局部变量。 DECLARE @var1 int , @var2 money 例  用 SET 语句和 SELECT 语句为局部变量赋值。 DECLARE @var1 datetime,@var2 char(10) SET @var1 = getdate() SELECT @var2 = convert(char(10),@var1,102) 例  用 SET 语句将查询结果赋给局部变量并用 SELECT 语句显示局部变量的值。 declare @date_var datetime set @date_var=(select min(birthday) from s) select @date_var as min_birthday
(2) 全局变量 全局变量:常被服务器用来跟踪服务器范围和特定会话期间的信息, 不能显式地被赋值或声明 。 全局变量 不能由用户定义 ,也不能被应用程序用来在处理器之间交叉传递信息。  9.1.2  常量与变量
①  @@rowcount @@rowcount 存储前一条命令影响到的记录总数,除了 DECLARE 语句之外,其他任何语句都可以影响 @@rowcount 的值。 例如 DECLARE @rows int SELECT @rows=@@rowcount 9.1.2  常量与变量
②  @@error 如果 @@error 为非 0 值,则表明执行过程中产生了错误,此时应当在程序中采取相应的措施加以处理。 @@error 的值与 @@rowcount 一样,会随着每一条 SQL Server 语句的变化而改变。 例 9-4  使服务器产生服务,并显示错误号。 raiserror('miscellaneous error message',16,1) /* 产生一个错误 */ if @@error<>0 SELECT @@error as 'last error' 运行结果: 服务器 :  消息  50000 ,级别  16 ,状态  1 ,行  1 miscellaneous error message last error 0  9.1.2  常量与变量
例 9-5  捕捉例 9-4 中服务器产生的错误号,并显示出来。 DECLARE @my_error int RAISERROR('miscellaneous error message',16,1) SELECT @my_error=@@error IF @my_error<>0 SELECT @my_error as 'last error' 运行结果: 服务器 :  消息  50000 ,级别  16 ,状态  1 ,行  2 miscellaneous error message last error 50000 9.1.2  常量与变量
③  @@trancount @@trancount 记录当前的事务数量,当某个事务当前并没有结束会话过程时, @@trancount 的值大于 0 。 ④  @@version @@version 的值代表服务器的当前版本和当前操作系统版本,是 SQL Server 中一项较实用的技术支持,通常对识别网络中某个未命名的服务器时非常有用。 ⑤  @@spid @@spid 返回当前用户进程的服务器进程 ID ,可以用来识别 sp_who 输出中的当前用户进程。
例 9-6  使用 @@spid 返回当前用户进程的 ID 。 SELECT @@spid as 'ID',SYSTEM_USER AS 'Login Name',USER AS 'User Name' 运行结果: ID  Login Name  User Name 52  sa   dbo 9.1.2  常量与变量
9.1.3  运算符与表达式 运算符用来执行数据列之间的数学运算或比较操作。 表达式是符号与运算符的组合。 简单的表达式可以是一个常量、变量、列或函数,复杂表达式是由运算符连接一个或多个简单表达式。 9.1  数据与表达式
1.  算术运算符与表达式 算术运算符用于数值型列或变量间的算术运算。算术运算符包括加 (+) 、减 (-) 、乘 (*) 、除 (/) 和取模 (%) 运算等。 例 9-9  使用“ +” 将 goods 表中高于 9000 的商品价格增加 15 元: SELECT goods_name,unit_price,(unit_price+15) AS nowprice  FROM goods WHERE unit_price>9000 运行结果如图所示。 9.1.3  运算符与表达式
2.  位运算符与表达式 位运算符用以对数据进行按位与 ( & ) 、或 (|) 、异或 (^) 、求反 (~) 等运算。 &  运算只有当两个表达式中的两个位值都为 1 时,结果中的位才被设置为 1 ,否则结果中的位被设置为 0 。 |  运算时,如果在两个表达式的任一位为 1 或者两个位均为 1 ,那么结果的对应位被设置为 1 ;如果表达式中的两个位都不为 1 ,则结果中该位的值被设置为 0 。 ^  运算时,如果在两个表达式中,只有一位的值为 1 ,则结果中位的值被设置为 1 ;如果两个位的值都为 0 或者都为 1 ,则结果中该位的值被清除为 0 。 9.1.3  运算符与表达式
例如, 170 与 75 进行 & 运算 先将 170 和 75 转换为二进制数 0000 0000 1010 1010 和 0000 0000 0100 1011 ,再进行 & 运算的结果是 0000 0000 0000 1010 ,即十进制数 10 。 同样,表达式 5^2 , ~1 , 5|2 的运算结果为: 7 , 0 , 7 。 9.1.3  运算符与表达式
3.  比较运算符与表达式 比较运算符用来比较两个表达式的值是否相同,可用于字符、数字或日期数据。 SQL Server 中的比较运算符有大于 (>) 、小于 (<) 、大于等于 (>=) 、小于等于 (<=) 和不等于 (!=) 等,比较运算返回布尔值,通常出现在条件表达式中。 比较运算符的结果为布尔数据类型,其值为 TRUE 、 FALSE 及 UNKNOWN 。 例如,表达式 2=3 的运算结果为 FALSE 。 9.1.3  运算符与表达式
4.  逻辑运算符与表达式 逻辑运算符与 (AND) 、或 (OR) 、非 (NOT) 等,用于对某个条件进行测试,以获得其真实情况。 逻辑运算符和比较运算符一样,返回 TRUE 或 FALSE 的布尔数据值。 9.1.3  运算符与表达式
表 9-5  逻辑运算符 9.1.3  运算符与表达式 如果子查询包含一些行,那么值为 TRUE 。 EXISTS 如果操作数在某个范围之内,那么值为 TRUE 。 BETWEEN 如果一系列的比较中任何一个为 TRUE ,那么值为 TRUE 。 ANY 如果一系列的比较都为 TRUE ,那么值为 TRUE 。 ALL 如果操作数等于表达式列表中的一个,那么值为 TRUE 。 IN 如果操作数与一种模式相匹配,那么值为 TRUE 。 LIKE 对任何其他布尔运算符的值取反。 NOT 如果两个布尔表达式中的一个为 TRUE ,那么结果就为 TRUE 。 OR 如果两个布尔表达式都为 TRUE ,那么结果为 TRUE 。 AND 含  义 运算符
逻辑运算符通常和比较运算一起构成更为复杂的表达式。逻辑运算符的操作数都只能是布尔型数据。 例如,在表 employee 中查找 1973 年以前与 1980 年以后出生的男员工的表达式为: (year(birth_date)<1973 OR year(birth_date)>1980) AND sex=' 男 ' 9.1.3  运算符与表达式
LIKE 运算符 确定给定的字符串是否与指定的模式匹配,通常只限于字符数据类型。 LIKE 的通配符如下表 9.1.3  运算符与表达式 employee_name LIKE '[^ 张李 ] 海燕 '  将查找不姓张、李的名为海燕的职员。 不属于指定范围 ([a-f ]) 或集合 ([abcdef ]) 的任何单个字符。 [^] employee_name LIKE '[ 张李王 ] 海燕 '  将查找张海燕、李海燕、王海燕等。 指定范围 ([a-f ]) 或集合 ([abcdef ]) 中的任何单个字符。 [ ] employee_name LIKE '_ 海燕 '  将查找以“海燕”结尾的所有 6 个字符的名字。 下划线,对应任何单个字符。 _ address LIKE '% 公司 %'  将查找地址任意位置包含公司的所有职员。 包含零个或多个字符的任意字符串。 % 示  例 描  述 运算符
例如 , 查找所有姓“钱”的员工及住址 SELECT employee_name, address FROM employee WHERE employee_name LIKE ' 钱 %' 9.1.3  运算符与表达式
4 .连接运算符与表达式 连接运算符 (+) 用于两个字符串数据的连接,通常也称为字符串运算符。 在 SQL Server 中,对字符串的其他操作通过字符串函数进行。字符串连接运算符的操作数类型有 char 、 varchar 和 text 等。 例如, ‘ Dr.’+‘Computer’ 的运算结果 :'Dr. Computer' 9.1.3  运算符与表达式
5 .运算符的优先级别 SQL Server 中各种运算符的优先顺序如下: ( ) -> ~ -> ^ ->  & ->  | -> * 、 / 、 % -> + 、 - -> NOT -> AND -> OR 排在前面的运算符的优先级高于其后的运算符。在一个表达式中,先计算优先级较高的运算,后计算优先级低的运算,相同优先级的运算按自左向右的顺序依次进行。  9.1.3  运算符与表达式
9.2  函  数 9.2.1  常用函数 9.2.2  用户定义函数
9.2.1  常用函数 函数是—组编译好的 Transact-SQL 语句,它们可以带一个或一组数值做参数,也可不带参数,它返回一个数值、数值集合,或执行一些操作。 函数能够重复执行一些操作,从而避免不断重写代码。 SQL Server 2000 支持两种函数类型: (1)  内置函数 : 是一组预定义的函数,是 Transact-SQL 语言的一部分,按 Transact-SQL 参考中定义的方式运行且不能修改。 (2)  用户定义函数 : 由用户定义的 Transact-SQL 函数。它将频繁执行的功能语句块封装到一个命名实体中,该实体可以由 Transact-SQL 语句调用。 9.2  函  数
1 .字符串函数  字符串函数用来实现对字符型数据的转换、查找、分析等操作,通常用做字符串表达式的一部分。表 9-7 中列出了 SQL Server 的常用字符串函数。 9.2.1  常用函数
(1)  使用 datalength 和 Len 函数 datalength 函数主要用于判断可变长字符串的长度,对于定长字符串将返回该列的长度。要得到字符串的真实长度,通常需要使用 rtrim 函数截去字符串尾部的空格。 Len 函数可以获取字符串的字符个数,而不是字节数,也不包含尾随空格。 9.2.1  常用函数
例 9-10  从表 department 中读取 manger 列的各记录的实际长度。 SELECT Datalength(rtrim(manger)) AS 'DATALENGTH',  Len(rtrim(manger)) AS 'LEN' FROM department 运行结果如下: DATALENGTH  LEN 4  2 6  3 6  3 6  3 9.2.1  常用函数
(2)  使用 Soundex 函数 soundex 函数将 char_expr 转换为 4 个字符的声音码,其中第一个码为原字符串的第一个字符,第 2~4 个字符为数字,是该字符串的声音字母所对应的数字,但忽略了除首字母外的串中的所有元音。 Soundex 函数可用来查找声音相似的字符串,但它对数字和汉字均只返回 0 值。 例如 SELECT soundex('1'),soundex('a'), soundex(' 计算机 '), soundex('abc'), soundex ('abcd'), soundex('a12c'),  soundex('a 数字 ') 返回值为: 0000  A000  0000  A120  A120  A000  A000 9.2.1  常用函数
(4)  使用 Charindex 函数实现串内搜索 charindex 函数主要用于在串内找出与指定串匹配的串,如果找到的话, charindex 函数返回第一个匹配的位置。格式: Charindex(expr1, expr2[, start_location])  expr1 是待查找的字符串 expr2 是用来搜索 expr1 子串的字符表达式, start_location 是在 expr2 中查找 expr1 的开始位置,如果此值省略、为负或为 0 ,均从起始位置开始查找。  9.2.1  常用函数
例如 SELECT charindex(',', 'red,white,blue')  该查询确定了字符串 'red,white,blue' 中第一个逗号的位置。 9.2.1  常用函数
(5)  使用 Patindex 函数 patindex 函数返回在指定表达式中模式第一次出现的起始位置,如果模式没有则返回 0 。格式: Patindex('%pattern%', expression)  pattern 是字符串, % 字符必须出现在模式的开头和结尾。 expression 通常是搜索指定子串的表达式或列。 例如: SELECT patindex('%abc%','abc123'), patindex('123','abc123')  子串“ abc” 和“ 123” 在字符串“ abc123” 中出现的起始位置分别为: 1 和 0 。因为子串“ 123” 不是以 % 开头和结尾。 9.2.1  常用函数
2 .数学函数 数学函数用来实现各种数学运算,如指数运算、对数运算、三角运算等,其操作数为数值型数据,如 int 、 float 、 real 、 money 等  表 9-8 列出了 SQL Server 的数学函数。 9.2.1  常用函数
例 9-11  在同一表达式中使用 sin 、 atan 、 rand 、 pi 、 sign 函数。 SELECT  sin(23.45), atan(1.234), rand(), pi(), sign(-2.34) 运行结果如下: -0.99374071017265964  0.88976244895918932  0.1975661765616786 3.1415926535897931  -1.00 9.2.1  常用函数
例 9-12  用 ceiling 和 floor 函数返回大于或等于指定值的最小整数值和小于或等于指定值的最大整数值。 SELECT  ceiling(123),floor(321),  ceiling(12.3), ceiling(-32.1),floor(-32.1) 运行结果如下: 123 321 13 -32 -33 9.2.1  常用函数
SELECT round(12.34512,3), round(12.34567,3), round(12.345,-2), round(54.321,-2) 运行结果如下: 12.34500  12.34600  .000  100.000 Round(numeric_expr, int_expr)  的 int_expr 为负数时,将小数点左边第 int_expr 位四舍五入。   例 9-13  round 函数的使用。 9.2.1  常用函数
3 .日期函数 日期函数用来操作 datetime 和 smalldatetime 类型的数据,执行算术运算。与其他函数一样,可以在 SELECT 语句和 WHERE 子句以及表达式中使用日期函数。  9.2.1  常用函数
表 9-9  SQL Server 的日期函数 9.2.1  常用函数 返回 date_expr 中的年份值 Year(date_expr) 返回 date_expr 中的月份值 Month(date_expr) 返回 date_expr 中的日期值 Day(date_expr) 返回以 datepart 指定方式表示的 date_expr 加上 number 以后的日期 Dateadd(datepart, number, date_expr) 以 datepart 指定的方式,返回 date_expr2 与 date_expr1 之差 Datediff(datepart,date_expr1,date_expr2) 以整数形式返回 date_expr 中的 datepart 指定部分 Datepart(datepart, date_expr) 以字符串形式返回 date_expr 中的指定部分,如果合适的话还将其转换为名称 ( 如 June) Datename(datepart, date_expr) 返回当前系统的日期和时间 Getdate() 描  述 函数名称及格式
表 9-10  SQL Server 的日期部分 9.2.1  常用函数 0~999 ms Millisecond 0~59 ss Second 0~59 mi Minute 0~23 hh Hour 1~7(Mon~Sun) dw Weekday 1~54 wk Week 1~31 dd Day 1~366 dy Dayofyear 1~12 mm Month 1~4 qq Quarter 1753~9999 yy Year 取值范围 写  法 日期部分
例 9-14  使用 datediff 函数来确定货物是否按时送给客户。 SELECT goods_id,datediff(dd,send_date,arrival_date) FROM purchase_order 为了从 datediff 中得到一个正值,应注意把较早的日期放在前面  9.2.1  常用函数
例 9-15  使用 datename 函数返回员工的出生日期的月份 (mm) 名称。 SELECT employee_name,datename(mm,birth_date) FROM employee 运行结果如下: 钱达理  December 东方牧  April 郭文斌  March 肖海燕  July 张明华  August 9.2.1  常用函数
4 .系统函数 系统函数用于获取有关计算机系统、用户、数据库和数据库对象的信息。与其他函数一样,可以在 SELECT 和 WHERE 子句以及表达式中使用系统函数。 表 9-11 列出了 SQL Server 的系统函数。 9.2.1  常用函数
例 9-16  使用 object_name 函数返回已知 ID 号的对象名。 SELECT object_name(469576711) 运行结果如下: Employee 9.2.1  常用函数
例 9-17  利用 object_id 函数,根据表名返回该表的 ID 号。 SELECT name FROM sysindexes WHERE id=object_id('customer') 运行结果如下: name customer _WA_Sys_customer_id_75D7831F 9.2.1  常用函数
9.2.2  用户定义函数 根据函数返回值形式的不同将用户定义函数分为 3 种类型。 (1)  标量函数 标量函数返回一个确定类型的标量值,其函数值类型为 SQL Server 的系统数据类型(除 text 、 ntext 、 image 、 cursor 、 timestamp 、 table 类型外)。函数体语句定义在 BEGIN…END 语句内。 (2)  内嵌表值函数 内嵌表值函数返回的函数值为一个表。内嵌表值函数的函数体不使用 BEGIN…END 语句,其返回的表是 RETURN 子句中的 SELECT 命令查询的结果集,其功能相当于一个参数化的视图。 (3)  多语句表值函数 多语句表值函数可以看作标量函数和内嵌表值函数的结合体。其函数值也是一个表,但函数体也用 BEGIN…END 语句定义,返回值的表中的数据由函数体中的语句插入。 9.2  函  数
1.  创建用户定义函数 (1)  使用 CREATE FUNCTION 创建用户定义函数 标量函数的语法格式: CREATE FUNCTION  [owner_name.] function_name ( [{ @parameter_name [AS] scalar_parameter_data_type[=default ] } [ ,...n ] ] )   RETURNS  scalar_return_data_type [ WITH < function_option> [ [,] ...n] ]  [ AS ] BEGIN function_body RETURN scalar_expression END 注:  function_option , ENCRYPTION : 加密函数, SCHEMABINDING : 不能更改或删除相关的数据库对象 9.2.2  用户定义函数
例 9-18  创建一个用户定义函数 DatetoQuarter ,将输入的日期数据转换为该日期对应的季度值。如输入 '2006-8-5' ,返回 '3Q2006' ,表示 2006 年 3 季度。 CREATE FUNCTION DatetoQuarter(@dqdate datetime) RETURNS char(6) AS BEGIN  RETURN(datename(q,@dqdate)+'Q'+datename(yyyy,@dqdate)) END 9.2.2  用户定义函数 例、创建工龄工资计算函数
调用函数 select emp.dbo.wage(‘1996-1-1’,getdate(),20) as work_year_wage 运行结果如下: 9.2.2  用户定义函数
内嵌表值函数的语法格式: CREATE FUNCTION [owner_name.] function_name ( [{@parameter_name [AS] scalar_parameter_data_type [=default ] } [ ,...n ] ] )  RETURNS TABLE   [ WITH < function_option > [ [,] ...n ] ]  [ AS ] RETURN [ ( ] select_stmt [ ) ] 9.2.2  用户定义函数
例 9-19  创建用户定义函数 goodsq ,返回输入商品编号的商品名称和库存量。 CREATE FUNCTION goodsq(@goods_id varchar(30)) RETURNS TABLE AS RETURN ( SELECT goods_name,stock_quantity FROM goods WHERE goods_id =@goods_id ) 9.2.2  用户定义函数
例 9-19  创建用户定义函数 goodsq ,返回输入商品编号的商品名称和库存量。 use northwind go CREATE FUNCTION goodsq(@product_id int) RETURNS TABLE AS RETURN(SELECT productid,productname FROM products WHERE productid=@product_id) 9.2.2  用户定义函数
多语句表值函数的语法格式: CREATE FUNCTION [owner_name.] function_name ( [ { @parameter_name [AS] scalar_parameter_data_type [ = default ] } [ ,...n ] ] )  RETURNS @return_variable TABLE < table_type_definition > [ WITH < function_option > [ [,] ...n ] ]  [ AS ] BEGIN function_body RETURN END 9.2.2  用户定义函数
例 9-20  根据输入的订单编号,返回该订单对应商品的编号、名称、类别编号、类别名称。 CREATE FUNCTION good_info(@in_o_id varchar(10)) RETURNS @goodinfo TABLE (o_id char(6), g_id char(6), g_name varchar(50)) AS BEGIN DECLARE @g_id varchar(10),@g_name varchar(30) SELECT @g_id=goods_id FROM sell_order1 WHERE order_id1=@in_o_id SELECT @g_name=goods_name FROM goods2 WHERE goods_id=@g_id INSERT INTO @goodinfo VALUES(@in_o_id,@g_id,@g_name) RETURN END 9.2.2  用户定义函数
①  在企业管理器中选择要创建用户定义函数的数据库(如 Sales ),在数据库对象“用户定义函数”项上单击右键,从弹出的快捷菜单中选择“新建用户定义的函数 ...” 选项,出现如图所示的“用户定义函数属性”对话框。 (2)  使用企业管理器创建用户定义函数 9.2.2  用户定义函数
②  在“用户定义函数属性”对话框的文本框中指定函数名称(如 numtostr ),编写函数的代码。 ③  单击“检查语法”按钮,出现“语法检查成功”消息框后,单击“确定”按钮,将用户定义函数对象添加到数据库中。 9.2.2  用户定义函数
2.  执行用户定义函数 使用函数需要指出函数所有者,即为函数加上所有者权限作为前缀。其语法格式如下:  [database_name.]owner_name.function_name ([argument_expr] [, ...] ) 9.2.2  用户定义函数
例如,调用例 9-18 创建的用户定义函数 DatetoQuarter SELECT dbo.DatetoQuarter ('2006-8-5') 运行结果为: 3Q2006 调用例 9-20 创建的用户定义函数 good_info ,使用以下语句: SELECT * FROM dbo.good_info('S00002') 运行结果为表的记录,如图 9-3 所示。 9.2.2  用户定义函数
3.  修改和删除用户定义函数 用企业管理器修改用户定义函数,选择要修改函数,单击右键,从快捷菜单中选择“属性”选项,打开图 9-2 所示的“用户定义函数属性”对话框。在该对话框中可以修改用户定义函数的函数体、参数等。从快捷菜单中选择“删除”选项,则可删除用户定义函数。 用 ALTER FUNCTION 命令也可以修改用户定义函数。此命令的语法与 CREAT FUNCTION 相同,使用 ALTER FUNCTION 命令相当于重建一个同名的函数。 使用 DROP FUNCTION 命令删除用户定义函数,其语法如下: DROP FUNCTION { [ owner_name .] function_name } [ ,...n ] 其中, function_name 是要删除的用户定义的函数名称。 9.2.2  用户定义函数
例如,删除例 9-18 创建的用户定义函数 DROP FUNCTON DatetoQuarter 删除用户定义函数时,可以不加所有者前缀。 9.2.2  用户定义函数
9.3  程序控制流语句 9.3.1  语句块和注释 9.3.2  选择控制 9.3.3  循环控制 9.3.4  批处理
9.3.1  语句块和注释 Transact-SQL 提供了控制流语言的特殊关键字和用于编写过程性代码的语法结构,可进行顺序、分支、循环、存储过程、触发器等程序设计,编写结构化的模块代码,并放置到数据库服务器上。 9.3  程序控制流语句
BEGIN...END 用来设定一个语句块,将在 BEGIN...END 内的所有语句视为一个逻辑单元执行。 语句块 BEGIN...END 的语法格式为: BEGIN { sql_statement | statement_block } END 1.  语句块 BEGIN...END 9.3.1  语句块和注释
USE employee GO DECLARE @linkman_name char(8) BEGIN SELECT @linkman_name=(SELECT linkman_name FROM customer WHERE customer_id LIKE ‘1001') SELECT @linkman_name END 例 9-21  显示 Sales 数据库中 customer 表的编号为‘ 1001' 的联系人姓名。 9.3.1  语句块和注释
在 BEGIN...END 中可嵌套另外的 BEGIN...END 来定义另一程序块。 例 9-22  语句块嵌套举例。 DECLARE @errorcode int,@nowdate dateTIME BEGIN SET @nowdate=getdate() INSERT sell_order(order_date,send_date,arriver_date,custom_id) VALUES(@nowdate,@nowdate+5,@nowdate+10,'C0002') SELECT @errorcode=@@error IF @errorcode>0 BEGIN RAISERROR(' 当表 sell_order 插入数据时发生错误 !',16,1) RETURN END END 9.3.1  语句块和注释
2.  注释 有两种方法来声明注释: 单行注释 多行注释。 9.3.1  语句块和注释
( 1 )单行注释 在语句中,使用两个连字符“ --” 开头,则从此开始的整行或者行的一部分就成为了注释,注释在行的末尾结束。 --This is a comment.Whole line will be ignored. SELECT employee_name, address  -- 查询所有姓钱的员工 FROM employee WHERE employee_name LIKE ' 钱 %' 注释的部分不会被 SQL Server 执行。 9.3.1  语句块和注释
( 2 )多行注释 多行注释方法是 SQL Server 自带特性,可以注释大块跨越多行的代码,它必须用一对分隔符“ /*  */” 将余下的其他代码分隔开。 /* This is a commnet . All these lines will be ignored. */ /* List all employees.*/ SELECT * FROM employee 注释并没有长度限制。 SQL Server 文档禁止嵌套多行注释,但单行注释可以嵌套在多行注释中。 /*  --List all employees. SELECT * FROM employee */ 9.3.1  语句块和注释
选择结构 选择结构可以使用条件语句来实现。 语法格式: IF < 布尔表达式 > <SQL 语句 >|< 语句块 > [ELSE <SQL 语句 >|< 语句块 >]
IF (SELECT avg(unit_price) FROM goods WHERE supplier_id='S001')>$9799.0 SELECT 'supplier_id 为 S001 的商品的平均单价比 9799 大 ' ELSE SELECT 'supplier_id 为 S001 的商品的平均单价比 9799 小 ' 运行结果如下: supplier_id 为 S001 的商品的平均单价比 9799 大 例 9-23  判断表 goods 中 supplier_id 为“ S001” 的商品的平均单价是否大于 9799 。 9.3.2  选择控制
DECLARE @lname varchar(40),@msg varchar(255) SELECT @lname=' 陈晓兵 ' IF EXISTS(SELECT * FROM department WHERE manager=@lname) BEGIN SELECT @msg=' 有人名为 '+@lname SELECT @msg END ELSE BEGIN SELECT @msg=' 没有人名为 '+@lname SELECT @msg END 运行结果为: 有人名为陈晓兵 例 9-24  用 EXISTS 确定表 department 中是否存在“陈晓兵”。 9.3.2  选择控制
例  查询学号为 1001 的学生。 脚本: if exists(select sno from s where sno='0001') print  ' 找到 ' else  print ' 未找到 ' 选择结构
例  查询标识号为’ 5678’ 的出版商出版的任何书的信息。 脚本: if exists(select * from title where pub_id=‘5678') begin print ‘ 该出版商包含如下书籍:‘ select * from title where pub_id=‘5678’ end else  print ' 未找到 ' 选择结构
例 9-25  嵌套 IF...ELSE 语句的使用。 IF (SELECT SUM(order_num) FROM sell_order)>50 PRINT ' 他们是最佳的客户 ' ELSE IF (SELECT SUM(order_num) FROM sell_order)>30 PRINT ' 必须与他们保持联络 ' ELSE PRINT ' 再想想办法吧 !!‘ 9.3.2  选择控制
例 查询学号为 1001 的学生选修的课程号为 C002 的成绩等级(条件语句的嵌套)。 脚本: 选择结构
查询在部门’办公室’的人员姓名 课堂练习:
2. CASE 函数 CASE 函数计算多个条件并为每个条件返回单个值。 (1)  简单 CASE 函数:将某个表达式与一组简单表达式进行比较以确定结果。 CASE input_expression WHEN  when_expression  THEN  result_expression [ ...n ] [ELSE else_result_expression ] END (2) CASE 搜索函数, CASE 计算一组逻辑表达式以确定结果。 CASE WHEN  Boolean_expression  THEN  result_expression [ ... n ] [ ELSE else_result_expression ] END 9.3.2  选择控制
例 9-26  使用简单 CASE 函数将 goods 表中的商品分类重命名,以使之更易理解。 SELECT CASE classification_id WHEN 'P001' THEN ' 笔记本计算机 ' WHEN 'P002' THEN ' 激光打印机 ' WHEN 'P003' THEN ' 喷墨打印机 ' WHEN 'P004' THEN ' 交换机 ' ELSE ' 没有这种品牌 ' END AS Classification,  goods_name AS 'Goods Name', unit_price AS Price FROM goods WHERE unit_price IS NOT NULL 9.3.2  选择控制
SELECT goods_name AS  商品名称 , CASE WHEN stock_quantity-order_quantity<=3 THEN ' 紧急进货 ' WHEN stock_quantity-order_quantity>3  and stock_quantity-order_quantity<=10 THEN ' 暂缓进货 ' WHEN stock_quantity-order_quantity>10 THEN ' 货物充足 ' END AS  进货判断 FROM goods 例 9-27  根据 goods 表中库存货物数量与订货量之差,使用 CASE 搜索函数判断该商品是否进货。 9.3.2  选择控制
上调员工工资,工资级别为 1 的上调 8% ,工资级别为 2 的上调 7% ,工资级别为 3 的上调 6% ,其他上调 5% 。 课堂练习:
循环结构 循环结构可以使用循环语句来实现。 语法格式: WHILE < 布尔表达式 > <SQL 语句 >|< 语句块 > 中断语句: BREAK 短路语句: CONTINUE
例 9-30  将 goods 表中库存数最大的商品每次订购 2 件,计算如此需要多少次订购才能使库存数不够一次订购。 DECLARE @count int,@maxstockid char(6),@maxstock float SET @count=0 SET @maxstock=(SELECT max(stock_quantity) FROM goods) SET @maxstockid=(SELECT goods_id FROM goods  WHERE stock_quantity=@maxstock) SELECT @maxstockid,@maxstock WHILE (@maxstock> (SELECT order_quantity FROM goods WHERE goods_id=@maxstockid)) BEGIN UPDATE goods  SET order_quantity=order_quantity+2  WHERE goods_id=@maxstockid SET @count=@count+1 END SELECT @count 运行结果如下: 8 9.3  程序控制流语句
DECLARE @x int,@y int,@c int SET @x=1,@y=1 WHILE @x<3 BEGIN print @x  WHILE @y<3 BEGIN SELECT @c=100*@x+@y print @c SELECT @y=@y+1 END SELECT @x=@x+1 SELECT @y=1 END 9.3  程序控制流语句 运行结果: 1 101 102 2 201 202
例 9-31  对于 goods 表,如果平均库存少于 12 , WHILE 循环就将各记录库存增加 5% ,再判断最高库存是否少于或等于 25 ,是则 WHILE 循环重新启动并再次将各记录库存增加 5% 。当循环不断地将库存增加直到最高库存超过 25 时,然后退出 WHILE 循环。 /* 执行循环,直到库存平均值超过 12*/ WHILE(SELECT avg(stock_quantity) FROM goods)<12 BEGIN UPDATE goods SET stock_quantity=stock_quantity*1.05 SELECT max(stock_quantity) FROM goods /* 如果最大库存值超过 25 ,则用 BREAK 退出 WHILE 循环,否则继续循环 */ IF(SELECT max(stock_quantity) FROM goods)>25 BEGIN PRINT ' 库存太多了 ' BREAK END ELSE CONTINUE END 9.3  程序控制流语句
BREAK 语句让程序跳出循环, CONTINUE 语句让程序跳过 CONTINUE 命令之后的语句,回到 WHILE 循环的第一行命令,重新开始循环。 BREAK 或 CONTINUE 语句 9.3  程序控制流语句
DECLARE @s int,@n int,@t int,@c int SET @s=0 SET @n=1 WHILE @n<=10 BEGIN  SET @c=1 SET @t=1 WHILE @c<=@n BEGIN SET @t=@t*@c SET @c=@c+1 END SET @s=@s+@t SET @n=@n+1 END SELECT @s,@n 例 9-32  计算 s=1!+2!+…+10! 。 9.3  程序控制流语句
1 、求 1~10 之间的素数和。 脚本: 2 、求 100~200 之间的全部素数。 脚本: 课堂练习:
转移语句 转移语句将程序的执行流程无条件转移到指定的标号处。 语法格式: GOTO < 标号 > 定义标号时,应在标号名后面加上冒号。 GOTO 语句常用在循环语句和条件语句内,使程序跳出循环或进行分支处理 。
例 9-11  求 10 的阶乘。 脚本: DECLARE @s int,@times int  set @s=1 set @times=1 label1: set @s=@s*@times set @times=@times+1 if @times<=10 goto label1 print ' 结果为 :'+str(@s)
例 9-28  使用 GOTO 语句改变程序流程。 DECLARE @x int SELECT @x=1 label_1: SELECT @x SELECT @x=@x+1 WHILE ( IF )   @x<6 GOTO label_1 9.3.2  选择控制
等待语句 等待语句挂起一个程序中语句的执行,直到指定的某一时间点到来或在一定的时间间断之后才继续执行。 语法格式: WAITFOR DELAY '< 时间间隔 >'|TIME '< 时间 >' 其中,时间间隔以及时间均为 datetime 类型,格式为“ hh:mm:ss” ,分别说明等待的时间长度和时间点,在 time 内不能指定日期。
例  设置等待一小时后执行查询。 脚本: begin waitfor delay '1:00:00' select * from s end  例 设置到十点整执行查询。 脚本: begin waitfor time '10:00:00' select * from s end 等待语句
返回语句 返回语句结束执行,使程序无条件返回,其后面的语句不再执行。 语法格式: RETURN [< 整数表达式 >] 存储过程可以使用 RETURN 语句向调用者返回一个整数值。在 SQL Server 2000 中,存储过程返回值为 0 时,表示存储过程成功执行。
例 9-29  RETURN 语句应用示例。 DECLARE @x int,@y int SELECT @x=1,@y=2 IF @x>@y  RETURN ELSE RETURN 9.3.2  选择控制
9.3.4  批处理 一个批处理是—条或多条 Transact-SQL 语句的集合。 SQL Server 服务器对批处理的处理分为四个阶段: 分析阶段,服务器检查命令的语法,验证表和列的名字的合法性 优化阶段,服务器确定完成一个查询的最有效的方法; 编译阶段,生成该批处理的执行计划; 运行阶段,—条一条地执行该批处理中的语句。 批处理最重要的特征就是它作为一个不可分的实体在服务器上解释和执行。在一些情况下批处理被隐式地设定。例如,用查询分析器来执行一组 Transact-SQL 语句,这组语句将被视为一个批处理来对待。 9.3  程序控制流语句
SQL Server 有以下几种指定批处理的方法。 (1)  应用程序作为一个执行单元发出的所有 SQL 语句构成一个批处理,并生成单个执行计划。 (2)  存储过程或触发器内的所有语句构成一个批处理。每个存储过程或触发器都编译为一个执行计划。 (3)  由 EXECUTE 语句执行的字符串是一个批处理,并编译为一个执行计划。例如, EXEC ('SELECT * FROM employee') (4)  由 sp_executesql 系统存储过程执行的字符串是一个批处理,并编译为一个执行计划。例如, execute sp_executesql N'SELECT * from Sales.dbo.employee'  1.  批处理的指定 9.3  程序控制流语句
GO 是批处理的结束标志。当编译器执行到 GO 时会把 GO 前面的所有语句当成一个批处理来执行。 GO 命令和 Transact-SQL 语句不可处在同一行上。但在 GO 命令行中可以包含注释。 在批处理的第一条语句后执行任何存储过程必须包含 EXECUTE 关键字。局部 ( 用户定义 ) 变量的作用域限制在一个批处理中,不可在 GO 命令后引用。 RETURN 可在任何时候从批处理中退出,而不执行位于 RETURN 之后的语句。 2.  批处理的结束与退出 9.3  程序控制流语句
USE Sales GO  --  批处理结束标志 CREATE VIEW employee_info AS SELECT * FROM employee GO -- CREATE VIEW 语句与其他语句隔离 SELECT * FROM employee_info GO  例 9-33  创建一个视图,使用 GO 命令将 CREATE VIEW 语句与批处理中的其他语句 ( 如 USE 、 SELECT 语句等 ) 隔离。 9.3  程序控制流语句
9.4  游标管理与应用 9.4.1  游标概述 9.4.2  声明游标 9.4.3  使用游标 9.4.4  游标的应用 9.4.5  使用系统存储过程管理游标
9.4.1  游标概述 1.  游标种类 (1)Transact-SQL 游标:是由 DECLARE CURSOR 语句定义,主要用在服务器上,由从客户端发送给服务器的 Transact-SQL 语句或批处理、存储过程、触发器中的 Transact-SQL 语句进行管理。 Transact-SQL 游标不支持提取数据块或多行数据。 (2)API 游标:支持在 OLE DB 、 ODBC 以及 DB_library 中使用游标函数,主要用在服务器上。每一次客户端应用程序调用 API 游标函数, SQL Server 的 OLE DB 提供者、 ODBC 驱动器或 DB_library 的动态链接库 (DLL) 都会将这些客户请求送给服务器以对 API 游标进行处理。 (3) 客户游标:当客户机缓存结果集时才使用。在客户游标中,有一个默认的结果集被用来在客户机上缓存整个结果集。客户游标仅支持静态游标。一般情况下,服务器游标能支持绝大多数的游标操作,但不支持所有的 Transact-SQL 语句或批处理,所以客户游标常常仅被用作服务器游标的辅助。 由于 API 游标和 Transact-SQL 游标使用在服务器端,所以被称为服务器游标或后台游标,而客户端游标被称为前台游标。 9.4  游标管理与应用
2.  服务器游标与默认结果集的比较 SQL Server 以两种方式为用户返回结果集:默认结果集和服务器游标。 (1) 默认结果集具有的特点:开销小;取数据时提供最大性能;仅支持默认的单进、只读游标功能;返回结果行时一次一行;连接时一次只支持一个活动语句;支持所有 Transact-SQL 语句。 (2) 服务器游标具有的持点:支持所有游标功能;可以为用户返回数据块;在单个连接上支持多个活动语句;以性能补偿游标功能;不支持所有返回多于一行结果集的 Transact-SQL 语句。 使用游标不如使用默认结果集的效率高。在默认结果集中,客户端只向服务器发送要执行的语句。而使用服务器游标时,每个 FETCH 语句都必须从客户端发往服务器,再在服务器中分析此语句并将它编译为执行汁划。 9.4.1  游标概述
3.  服务器游标与客户端游标的比较 使用服务器游标比使用客户游标有以下几方面的优点: (1) 性能:如果要在游标中访问部分数据,使用服务器游标将提供最佳性能,因为只有被取到的数据在网络上发送,客户游标在客户端存取所有结果集。 (2) 更准确的定位更新:服务器游标直接支持定位操作,如 UPDATE 和 DELETE 语句,客户游标通过产生 Transact-SQL 搜索 UPDATE 语句模拟定位游标更新,如果多行与 UPDATE 语句的 WHERE 子句的条件相匹配将导致无意义更新。 (3) 内存使用:使用服务器游标时,客户端不需要高速存取大量数据或者保持有关游标位置的信息,这些都由服务器来完成。 (4) 多活动语句:使用服务器游标时,结果不会存留在游标操作之间的连接上,这就允许同时拥有多个活动的基于游标的语句。 9.4.1  游标概述
SQL Server 支持 4 种类型的服务器游标,它们是单进游标、静态游标、动态游标和键集驱动游标。 (1) 单进游标只支持游标按从前向后顺序提取数据,游标从数据库中提取一条记录并进行操作,操作完毕后,再提取下一条记录。 (2) 静态游标也称为快照游标,它总是按照游标打开时的原样显示结果集,并不反映在数据库中对任何结果集成员所做的修改,因此不能利用静态游标修改基表中的数据。静态游标打开时的结果集存储在数据库 tempdb 中。静态游标始终是只读的。 (3) 动态游标也称为敏感游标,与静态游标相对,当游标在结果集中滚动时,结果集中的数据记录的数据值、顺序和成员的变化均反映到游标上,用户所做的各种操作均可通过游标反映。 (4) 键集驱动游标介于静态游标和动态游标之间,兼有两者的特点。打开键集驱动游标后,游标中的成员和行顺序是固定的。键集驱动游标由一套惟一标识符控制,这些惟一标识符就是键集。用户对基表中的非关键值列插入数据或进行修改造成数据值的变化,在整个游标中都是可见。键集驱动游标的键集在游标打开时建立在数据库 tempdb 中。 4.  服务器游标类型 9.4.1  游标概述
9.4.2  声明游标 SQL Server 游标具有下面的处理过程: (1) 声明游标,定义其特性,如游标中的行是否可以被更新。 (2)  执行 Transact-SQL 语句生成游标。 (3) 在游标中检索要查看的行。从游标中检索一行或几行的操作称为取数据。向前或向后执行取数据操作来检索行的行为称为滚动。 (4) 关闭游标。 9.4  游标管理与应用
声明客户端游标的步骤 创建一个新的  Visual Basic  项目,添加该  DataGrid  控件,然后添加数据环境。 配置为使用  Microsoft Jet 4.0 OLE DB  提供程序数据环境,并选择数据库文件  Nwind.mdb 。 (数据库文件随  Visual Basic 6.0  和典型的目录是  C:\Program Files \Microsoft Visual Studio\VB98\NWIND.MDB 。 创建新命令,产品,如打开表。  打开新创建的  Command  属性 对话框中,然后单击 高级 选项会确保设置该命令以使用客户端游标和  BatchOptimistic  锁定类型。 配置数据环境和命令的  DataGrid  控件的  DataSource  和  DataMemeber  属性。 运行该项目。 修改第一行,第二列  (ProductName)  的单元格中的值。 单击所的  DataGrid  中的另一个单元格并请注意更改更新到数据库无需调用  UpdateBatch  注意 : 通过重新运行该程序验证更改。 9.4  游标管理与应用
语法格式如下: DECLARE cursor_name [ INSENSITIVE ] [ SCROLL ] CURSOR  FOR select_statement  [ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ] 1. SQL-92 游标定义格式 9.4.2  声明游标
表 9-13  SCROLL 的取值 9.4.2  声明游标 提取游标中的第 n 行数据 ABSULUTE n 提取游标当前位置之前或之后的第 n 行数据 (n 为正表示向后, n 为负表示向前 ) RELATIVE n 提取游标当前位置的下一行数据 NEXT 提取游标当前位置的上一行数据 PRIOR 提取游标中的最后一行数据 LAST 提取游标中的第一行数据 FIRST 含  义 SCROLL 选项
例 9-34  使用 SQL-92 标准的游标声明语句声明一个游标,用于访问 Sales 数据库中的 goods 表的信息。 USE Sales GO DECLARE Goods_cursor CURSOR FOR  SELECT * FROM Goods FOR READ ONLY 9.4.2  声明游标
语法格式如下: 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 ] ] ] 2. Transact-SQL 扩展游标定义格式 9.4.2  声明游标
例 9-35  为 customer 表定义一个全局滚动动态游标,用于访问顾客的编号、姓名、地址、电话信息。 DECLARE cur_customer CURSOR GLOBAL SCROLL DYNAMIC FOR SELECT customer_id,customer_name,address,telphone FROM customer 9.4.2  声明游标
9.4.3  使用游标 1.  打开游标 游标声明之后,必须打开才能使用。打开游标的语法格式如下: OPEN {{[GLOBAL] cursor_name} | cursor_variable_name } 例如,打开例 9-35 所声明的游标操作: OPEN cur_customer 9.4  游标管理与应用
2.  读取游标 一旦游标被成功打开,就可以从游标中逐行地读取数据,以进行相关处理。从游标中读取数据主要使用 FETCH 命令。其语法格式为: FETCH [ [NEXT|PRIOR|FIRST|LAST |ABSOLUTE {n|@nvar} |RELATIVE {n|@nvar}]  FROM]  {{[GLOBAL] cursor_name}| cursor_variable_name }  [INTO @variable_name [ ,...n ]]  9.4.3  使用游标
表 9-14  @@fetch_status 变量 9.4.3  使用游标 所读取的数据已经不存在。 -2 FETCH 命令失败或者行数据己超出了结果集 -1 FETCH 命令已成功执行 0 描  述 返回值
OPEN cur_customer FETCH NEXT FROM cur_customer  /* 取第一个数据行 */ WHILE @@fetch_status = 0  /*  检查 @@fetch_status 是否还有数据可取 */ BEGIN FETCH NEXT FROM cur_customer END 例 9-36  打开例 9-35 中声明的游标,读取游标中的数据。 9.4.3  使用游标
CLOSE 的语法格式为: CLOSE {{[ GLOBAL ] cursor_name } | cursor_variable_name } 例如,关闭例 9-35 中的游标 cur_customer 的命令: CLOSE cur_customer 游标 cur_customer 在关闭后,仍可用 OPEN 语句打开继续读取数据行。 3.  关闭游标 9.4.3  使用游标
DEALLOCATE 命令删除游标与游标名或游标变量之间的联系,并且释放游标占用的所有系统资源。其语法格式为: DEALLOCATE {{ [ GLOBAL ] cursor_name } | cursor_variable_name } 一旦某个游标被删除,在重新打开之前,必需再次对其进行声明。 例如,释放例 9-35 所定义的游标 cur_customer 。 DEALLOCATE cur_customer DEALLOCATE cursor_variable_name 语句只删除对游标命名变量的引用。直到批处理、存储过程或触发器结束时变量离开作用域,才释放变量。 4.  释放游标 9.4.3  使用游标
9.4.4  游标的应用 1.  用游标修改和删除表数据 定位修改游标数据的语法格式为: UPDATE table_name  SET {column_name={expression | DEFAULT | NULL }[, ... n] WHERE CURRENT OF {{[GLOBAL] cursor_name} | cursor_variable_name} 删除游标数据的语法格式为: DELETE FROM table_name WHERE CURRENT OF {{[GLOBAL] cursor_name} | cursor_variable_name} 9.4  游标管理与应用
例 9-37  定义游标 cur_customer ,通过 cur_customer 更新 customer 表中的 customer_name 和 linkman_name 列。 DECLARE cur_customer CURSOR FOR SELECT * FROM customer FOR UPDATE OF customer_name,linkman_name  /* 该两列可更新 */ OPEN cur_customer  /* 打开 cur_customer 游标 */ FETCH NEXT FROM cur_customer  /* 将第一行数据放入缓冲区,以便更新操作 */ UPDATE customer SET customer_name=' 南方体育用品公司 ',linkman_name=' 李强 ' WHERE CURRENT OF cur_customer CLOSE cur_customer  /* 关闭 cur_customer 游标 */  9.4.4  游标的应用
若要删除 customer 表的一行数据,则使用以下命令替换例 9-37 中的 UPDATE 语句,就可以删除通过游标读入的一行数据。 DELETE FROM customer WHERE CURRENT OF cur_customer 9.4.4  游标的应用
2.  使用游标变量 CURSOR 关键字还可以作为变量类型来使用,此时,必须要将 CURSOR 进行变量声明。其语法格式为: DECLARE {@cursor_variable_name CURSOR } [ ,...n] 9.4.4  游标的应用
游标与一个游标变量相关联的方法  (1)  分别定义游标变量与游标,再将游标赋给游标变量。 DECLARE @cur_var CURSOR /* 定义游标变量 */ DECLARE cur_customer CURSOR FOR SELECT * FROM customer /* 定义游标 */ SET @cur_var=cur_customer  /* 设置游标与游标变量的关联 */ 9.4.4  游标的应用
(2)  定义游标变量后,通过 SET 命令直接创建游标与游标变量关联 DECLARE @cur_var CURSOR    /* 定义游标变量 */ SET @cur_var=CURSOR SCROLL KEYSET FOR SELECT * FROM customer  /* 创建游标并与游标变量的关联 */ 9.4.4  游标的应用
DECLARE @cur_var cursor SET @cur_var =cur_customer OPEN @cur_var FETCH NEXT FROM @cur_var CLOSE @cur_var DEALLOCATE @cur_var 例 9-38  通过游标变量来操作例 9-35 所声明的游标 cur_customer ,操作完成后删除游标变量。 9.4.4  游标的应用
在游标定义语句中使用了关键字 SCROLL ,则可以用 FETCH 语句在游标集合内向前或向后移动,也可以直接跳到集合的某一条记录。 例 9-39  定义可以任意移动的游标。 DECLARE cur_customer CURSOR  SCROLL READ_ONLY FOR  SELECT * FROM customer 3.  滚动游标 9.4.4  游标的应用
用 FETCH 语句将记录指针滚动记录 OPEN cur_customer FETCH NEXT FROM cur_customer FETCH PRIOR FROM cur_customer FETCH FIRST FROM cur_customer FETCH LAST FROM cur_customer FETCH ABSOLUTE 5 FROM cur_customer FETCH RELATIVE -2 FROM cur_customer 9.4.4  游标的应用
9.4.5  使用系统存储过程管理游标 1. sp_cursor_list sp_cursor_list 显示在当前作用域内的游标及其属性 sp_cursor_list [@cursor_return=]cursor_variable_name OUTPUT, [@cursor_scope=]cursor_scope 9.4  游标管理与应用 表示 LOCAL 、 GLOBAL 游标都返回 3 表示返回所有的 GLOBAL 游标 2 表示返回所有的 LOCAL 游标 1 描  述 cursor_scope 值 表 9-15  游标作用域范围值
例 9-40  声明一个键值驱动游标,并使用  sp_cursor_list  报告该游标的特性。 DECLARE employee_cur CURSOR KEYSET  FOR SELECT employee_name FROM Employee WHERE employee_name LIKE ' 肖 %' OPEN employee_cur DECLARE @report CURSOR  /* 声明游标 @report 存储来自 sp_cursor_list 的信息  */ /* 执行 sp_cursor_list  将信息送游标变量 @report*/ EXEC master.dbo.sp_cursor_list @cursor_return = @report OUTPUT,@cursor_scope = 2 FETCH NEXT FROM @report WHILE (@@fetch_status <> -1) BEGIN FETCH NEXT FROM @report END CLOSE @report DEALLOCATE @report GO CLOSE employee_cur DEALLOCATE employee_cur GO 9.4.5  使用系统存储过程管理游标
sp_describe_cursor 用来显示游标的属性。 sp_describe_cursor [ @cursor_return =] output_cursor_variable OUTPUT {  [, [ @cursor_source = ] N'local', [@cursor_identity = ] N'local_cursor_name' ] | [, [ @cursor_source = ] N'global', [ @cursor_identity = ] N'global_cursor_name' ] | [, [ @cursor_source = ] N'variable', [ @cursor_identity = ] N'input_cursor_variable' ]  } 2. sp_describe_cursor 9.4.5  使用系统存储过程管理游标
例 9-41  定义并打开一个全局游标,使用 sp_describe_cursor 报告游标的特性。 DECLARE employee_cur CURSOR STATIC FOR SELECT employee_name FROM employee OPEN employee_cur DECLARE @report CURSOR EXEC master.dbo.sp_describe_cursor @cursor_return = @Report OUTPUT, @cursor_source = N'global', @cursor_identity = N'employee_cur' FETCH NEXT from @report WHILE (@@FETCH_STATUS <> -1) BEGIN FETCH NEXT from @report END CLOSE @report DEALLOCATE @report GO CLOSE employee_cur DEALLOCATE employee_cur GO 9.4.5  使用系统存储过程管理游标
本章小结 数据类型是学习 Transact-SQL 语言的基础,不同种类的数据类型具有不同的精度和取值范围。 (2)  变量分为局部变量和全局变量两种。局部变量由 DECLARE 语句声明,可以由 SET 、 SELECT 或 UPDATE 语句赋值;全局变量不可由用户定义。 (3) Transact-SQL 的运算符分为算术运算符、位运算符、比较运算符、逻辑运算符、连接运算符,每种运算符都有专门的数据类型或操作数,各运算符间遵循一定的优先级。 (4)  函数是—组编译好的 Transact-SQL 语句。 SQL Server 2000 支持的函数分为内置函数和用户定义函数两种类型。用户定义函数可以通过企业管理器和 Transact-SQL 语句来管理。 CREATE FUNCTION 、 ALTER FUNCTION 、 DROP FUNCTION 分别创建、修改、删除用户定义函数。用户定义函数的调用要在函数名前加所有者作为前缀。 (5)  程序控制流语句 BEGIN 和 END 要一起使用,其功能是将语句块括起来。 IF…ELSE 语句根据条件来执行语句块。当程序有多个条件需要判断时,可以使用 CASE 函数实现。 WHILE 循环可根据条件多次重复执行语句。 GOTO 语句会破坏程序结构化的特点,尽量不要使用。 (6)  游标是应用程序通过行来管理数据的一种方法。有 3 种游标: Transact-SQL 游标、 API 服务器游标和客户游标。游标声明使用 DECLARE CURSOR 语句,游标的使用包括打开游标、读取数据、关闭游标、删除游标等,分别使用 OPEN 、 FETCH 、 CLOSE 、 DEARLLOCATE 语句。

第9章 transact sql程序设计

  • 1.
    第 9 章 Transact-SQL 程序设计
  • 2.
    本章内容 9.1 数据与表达式 9.2 函 数 9.3 程序控制流语句 9.4 游标管理与应用
  • 3.
    9.1 数据与表达式9.1.1 用户定义数据类型 9.1.2 常量与变量 9.1.3 运算符与表达式
  • 4.
    9.1.1 用户定义数据类型1 .使用系统存储过程来创建用户定义数据类型 , 命令格式如下: sp_addtype [@typename=] type, [@phystype = ] system_data_type [, [ @nulltype = ] 'null_type' ] [, [ @owner = ] 'owner_name' ] 9.1 数据与表达式
  • 5.
    例如,为 Sales 数据库创建—个不允许为NULL 值的 test_add 用户定义数据类型。 USE Sales GO EXEC sp_addtype test_add,'Varchar(10)','NOT NULL' GO 此后, test_add 可用为数据列或变量的数据类型。 9.1.1 用户定义数据类型
  • 6.
    2 .使用企业管理器创建用户定义数据类型 在企业管理器中,为Sales 数据库创建—个不允许 NULL 值的 test_add 用户定义数据类型,操作步骤如下。 (1) 选择 Sales 数据库。 (2) 在左窗格中选择“用户定义的数据类型”项,单击鼠标右键,在出现的快捷菜单中选择“新建用户定义数据类型”命令。 (3) 在“用户定义的数据类型属性”对话框中的文本框内输入 test_add 。 (4) 在“数据类型”下拉列表框中,选择 char 。 (5) 在“长度”文本框中输入 10 。 (6) 选中“允许 NULL 值”复选框。 (7) 单击“确定”按钮完成创建用户自定义数据类型。 9.1.1 用户定义数据类型
  • 7.
    9.1.2 常量与变量在程序运行中保持常值的数据,即程序本身不能改变其值的数据,称为常量,在程序中经常直接使用文字符号表示。 相应地,在程序运行过程中可以改变其值的数据,称为变量。 9.1 数据与表达式
  • 8.
    1 .常量 常量是表示特定数据值的符号,其格式取决于其数据类型(1) 字符串和二进制常量 字符串常量括在单引号内并包含字母数字字符 (a-z 、 A-Z 和 0-9) 以及特殊字符,如感叹号 (!) 、 at 符 (@) 和数字号 (#) 。 例如:‘ Cincinnati’ 、 ‘ Process X is 50% complete.’ 为字符串常量。 二进制常量具有前辍 0x 并且是十六进制数字字符串,它们不使用引号。例如 0xAE 、 0x12Ef 、 0x69048AEFDD010E 、 0x( 空串 ) 为二进制常量。 9.1.2 常量与变量
  • 9.
    (2) 日期 /时间常量 datetime 常量使用特定格式的字符日期值表示,用单引号括起来。 输入时,可以使用“ /” 、“ .” 、“ -” 作日期 / 时间常量的分隔符。 9.1.2 常量与变量 2005-09-03 00:00:00 2005-09-03 00:00:00.000 9/3/2005 1900-01-01 13:25:00 1900-01-01 13:25:19.000 13:25:19 2005-09-03 13:00:00 2005-09-03 13:00:00.000 9.3.2005 13:00 2005-09-03 13:00:00 2005-09-03 13:00:00.000 9/3/2005 1PM 2005-09-03 01:35:00 2005-09-03 01:34:34.123 Sep 3, 2005 1:34:34.122 Smalldatetime 值 datetime 值 输入格式
  • 10.
    (3) 数值常量 ①整型常量 --- 由没有用引号括起来且不含小数点的一串数字表示。例如, 1894 、 2 为整型常量。 ② 浮点常量 --- 采用科学记数法表示,例如, 101.5E5 、 0.5E-2 为浮点常量。 ③ 精确数值常量 --- 由没有用引号括起来且包含小数点的一串数字表示。例如, 1894.1204 、 2.0 为精确数值常量。 ④ 货币常量 --- 以“ $” 为前缀的一个整型或实型常量数据,不使用引号。例如, $12.5 、 $542023.14 为货币常量。 ⑤ uniqueidentifier 常量 --- 表示全局惟一标识符 GUID 值的字符串。可以使用字符或二进制字符串格式指定。 9.1.2 常量与变量
  • 11.
    (4) 逻辑数据常量 逻辑数据常量使用数字0 或 1 表示,并且不使用引号。非 0 的数字当作 1 处理。 (5) 空值 在数据列定义之后,还需确定该列是否允许空值 (NULL) 。允许空值意味着用户在向表中插入数据时可以忽略该列值。空值可以表示整型、实型、字符型数据。 9.1.2 常量与变量
  • 12.
    变量用于临时存放数据,变量中的数据随着程序的运行而变化,变量有名字与数据类型两个属性。 变量的命名使用常规标识符,即以字母、下划线 (_)、 at 符号 (@) 、数字符号 (#) 开头,后续字母、数字、 at 符号、美元符号 ($) 、下划线的字符序列。不允许嵌入空格或其他特殊字符。 2 .变量 9.1.2 常量与变量
  • 13.
    全局变量由系统定义并维护,通过在名称前面加“ @@” 符号局部变量的首字母为单个“ @” 。 全局变量和局部变量
  • 14.
    (1) 局部变量 局部变量使用DECLARE 语句定义 DECLARE {@local_variable data_type }[,...n] 变量名最大长度为 30 个字符。一条 DECLARE 语句可以定义多个变量,各变量之间使用逗号隔开。 例如 DECLARE @name varchar(30),@type int 9.1.2 常量与变量
  • 15.
    局部变量的赋值 局部变量用 select、 print 显示,用 select 、 update 、 set 赋值 ① 用 SELECT 为局部变量赋值 SELECT @variable_name=expression[ , … n] FROM … WHERE … 例如 DECLARE @int_var int SELECT @int_var =12 /* 给 @int_var 赋值 */ SELECT @int_var /* 将 @int_var 的值输出到屏幕上 */
  • 16.
    在一条语句中可以同时对几个变量进行赋值 例如 DECLARE@LastName char(8),@Firstname char(8),@BirthDate datetime SELECT @LastName='Smith',@Firstname='David',@BirthDate='1985-2-20' SELECT @LastName,@Firstname,@BirthDate 局部变量没有被赋值前,其值是 NULL ,若要在程序中引用它,必须先赋值。 9.1.2 常量与变量
  • 17.
    例 9-1 使用 SELECT 语句从 customer 表中检索出顾客编号为“ C0002” 的行,再将顾客的名字赋给变量 @customer 。 DECLARE @customer varchar(40),@curdate datetime SELECT @customer=customer_name,@curdate=getdate() FROM customer WHERE customer_id='C0002' 9.1.2 常量与变量
  • 18.
    ② 利用 UPDATE为局部变量赋值 例 9-2 将 sell_order 表中的 transporter_id 列值为“ T001” 、 goods_id 列值为“ G00003” 的 order_num 列的值赋给局部变量 @order_num 。 DECLARE @order_num float UPDATE sell_order SET @order_num=order_num*2 WHERE transporter_id='T001' AND goods_id='G00003' 9.1.2 常量与变量
  • 19.
    ③ 用SET 给局部变量赋值 SET 语句格式为: SET {@local_variable=expression} 使用 SET 初始化变量的方法与 SELECT 语句相同, 但一个 SET 语句只能为一个变量赋值 。 例 9-3 计算 employee 表的记录数并赋值给局部变量 @rows 。 DECLARE @rows int SET @rows=(SELECT COUNT(*) FROM employee) SELECT @rows 9.1.2 常量与变量
  • 20.
    例 声明一个datetime 类型的局部变量。 DECLARE @date_var datetime 例 声明两个局部变量。 DECLARE @var1 int , @var2 money 例 用 SET 语句和 SELECT 语句为局部变量赋值。 DECLARE @var1 datetime,@var2 char(10) SET @var1 = getdate() SELECT @var2 = convert(char(10),@var1,102) 例 用 SET 语句将查询结果赋给局部变量并用 SELECT 语句显示局部变量的值。 declare @date_var datetime set @date_var=(select min(birthday) from s) select @date_var as min_birthday
  • 21.
    (2) 全局变量 全局变量:常被服务器用来跟踪服务器范围和特定会话期间的信息,不能显式地被赋值或声明 。 全局变量 不能由用户定义 ,也不能被应用程序用来在处理器之间交叉传递信息。 9.1.2 常量与变量
  • 22.
    ① @@rowcount@@rowcount 存储前一条命令影响到的记录总数,除了 DECLARE 语句之外,其他任何语句都可以影响 @@rowcount 的值。 例如 DECLARE @rows int SELECT @rows=@@rowcount 9.1.2 常量与变量
  • 23.
    ② @@error如果 @@error 为非 0 值,则表明执行过程中产生了错误,此时应当在程序中采取相应的措施加以处理。 @@error 的值与 @@rowcount 一样,会随着每一条 SQL Server 语句的变化而改变。 例 9-4 使服务器产生服务,并显示错误号。 raiserror('miscellaneous error message',16,1) /* 产生一个错误 */ if @@error<>0 SELECT @@error as 'last error' 运行结果: 服务器 : 消息 50000 ,级别 16 ,状态 1 ,行 1 miscellaneous error message last error 0 9.1.2 常量与变量
  • 24.
    例 9-5 捕捉例 9-4 中服务器产生的错误号,并显示出来。 DECLARE @my_error int RAISERROR('miscellaneous error message',16,1) SELECT @my_error=@@error IF @my_error<>0 SELECT @my_error as 'last error' 运行结果: 服务器 : 消息 50000 ,级别 16 ,状态 1 ,行 2 miscellaneous error message last error 50000 9.1.2 常量与变量
  • 25.
    ③ @@trancount@@trancount 记录当前的事务数量,当某个事务当前并没有结束会话过程时, @@trancount 的值大于 0 。 ④ @@version @@version 的值代表服务器的当前版本和当前操作系统版本,是 SQL Server 中一项较实用的技术支持,通常对识别网络中某个未命名的服务器时非常有用。 ⑤ @@spid @@spid 返回当前用户进程的服务器进程 ID ,可以用来识别 sp_who 输出中的当前用户进程。
  • 26.
    例 9-6 使用 @@spid 返回当前用户进程的 ID 。 SELECT @@spid as 'ID',SYSTEM_USER AS 'Login Name',USER AS 'User Name' 运行结果: ID Login Name User Name 52 sa dbo 9.1.2 常量与变量
  • 27.
    9.1.3 运算符与表达式运算符用来执行数据列之间的数学运算或比较操作。 表达式是符号与运算符的组合。 简单的表达式可以是一个常量、变量、列或函数,复杂表达式是由运算符连接一个或多个简单表达式。 9.1 数据与表达式
  • 28.
    1. 算术运算符与表达式算术运算符用于数值型列或变量间的算术运算。算术运算符包括加 (+) 、减 (-) 、乘 (*) 、除 (/) 和取模 (%) 运算等。 例 9-9 使用“ +” 将 goods 表中高于 9000 的商品价格增加 15 元: SELECT goods_name,unit_price,(unit_price+15) AS nowprice FROM goods WHERE unit_price>9000 运行结果如图所示。 9.1.3 运算符与表达式
  • 29.
    2. 位运算符与表达式位运算符用以对数据进行按位与 ( & ) 、或 (|) 、异或 (^) 、求反 (~) 等运算。 & 运算只有当两个表达式中的两个位值都为 1 时,结果中的位才被设置为 1 ,否则结果中的位被设置为 0 。 | 运算时,如果在两个表达式的任一位为 1 或者两个位均为 1 ,那么结果的对应位被设置为 1 ;如果表达式中的两个位都不为 1 ,则结果中该位的值被设置为 0 。 ^ 运算时,如果在两个表达式中,只有一位的值为 1 ,则结果中位的值被设置为 1 ;如果两个位的值都为 0 或者都为 1 ,则结果中该位的值被清除为 0 。 9.1.3 运算符与表达式
  • 30.
    例如, 170 与75 进行 & 运算 先将 170 和 75 转换为二进制数 0000 0000 1010 1010 和 0000 0000 0100 1011 ,再进行 & 运算的结果是 0000 0000 0000 1010 ,即十进制数 10 。 同样,表达式 5^2 , ~1 , 5|2 的运算结果为: 7 , 0 , 7 。 9.1.3 运算符与表达式
  • 31.
    3. 比较运算符与表达式比较运算符用来比较两个表达式的值是否相同,可用于字符、数字或日期数据。 SQL Server 中的比较运算符有大于 (>) 、小于 (<) 、大于等于 (>=) 、小于等于 (<=) 和不等于 (!=) 等,比较运算返回布尔值,通常出现在条件表达式中。 比较运算符的结果为布尔数据类型,其值为 TRUE 、 FALSE 及 UNKNOWN 。 例如,表达式 2=3 的运算结果为 FALSE 。 9.1.3 运算符与表达式
  • 32.
    4. 逻辑运算符与表达式逻辑运算符与 (AND) 、或 (OR) 、非 (NOT) 等,用于对某个条件进行测试,以获得其真实情况。 逻辑运算符和比较运算符一样,返回 TRUE 或 FALSE 的布尔数据值。 9.1.3 运算符与表达式
  • 33.
    表 9-5 逻辑运算符 9.1.3 运算符与表达式 如果子查询包含一些行,那么值为 TRUE 。 EXISTS 如果操作数在某个范围之内,那么值为 TRUE 。 BETWEEN 如果一系列的比较中任何一个为 TRUE ,那么值为 TRUE 。 ANY 如果一系列的比较都为 TRUE ,那么值为 TRUE 。 ALL 如果操作数等于表达式列表中的一个,那么值为 TRUE 。 IN 如果操作数与一种模式相匹配,那么值为 TRUE 。 LIKE 对任何其他布尔运算符的值取反。 NOT 如果两个布尔表达式中的一个为 TRUE ,那么结果就为 TRUE 。 OR 如果两个布尔表达式都为 TRUE ,那么结果为 TRUE 。 AND 含 义 运算符
  • 34.
    逻辑运算符通常和比较运算一起构成更为复杂的表达式。逻辑运算符的操作数都只能是布尔型数据。 例如,在表 employee中查找 1973 年以前与 1980 年以后出生的男员工的表达式为: (year(birth_date)<1973 OR year(birth_date)>1980) AND sex=' 男 ' 9.1.3 运算符与表达式
  • 35.
    LIKE 运算符 确定给定的字符串是否与指定的模式匹配,通常只限于字符数据类型。LIKE 的通配符如下表 9.1.3 运算符与表达式 employee_name LIKE '[^ 张李 ] 海燕 ' 将查找不姓张、李的名为海燕的职员。 不属于指定范围 ([a-f ]) 或集合 ([abcdef ]) 的任何单个字符。 [^] employee_name LIKE '[ 张李王 ] 海燕 ' 将查找张海燕、李海燕、王海燕等。 指定范围 ([a-f ]) 或集合 ([abcdef ]) 中的任何单个字符。 [ ] employee_name LIKE '_ 海燕 ' 将查找以“海燕”结尾的所有 6 个字符的名字。 下划线,对应任何单个字符。 _ address LIKE '% 公司 %' 将查找地址任意位置包含公司的所有职员。 包含零个或多个字符的任意字符串。 % 示 例 描 述 运算符
  • 36.
    例如 , 查找所有姓“钱”的员工及住址SELECT employee_name, address FROM employee WHERE employee_name LIKE ' 钱 %' 9.1.3 运算符与表达式
  • 37.
    4 .连接运算符与表达式 连接运算符(+) 用于两个字符串数据的连接,通常也称为字符串运算符。 在 SQL Server 中,对字符串的其他操作通过字符串函数进行。字符串连接运算符的操作数类型有 char 、 varchar 和 text 等。 例如, ‘ Dr.’+‘Computer’ 的运算结果 :'Dr. Computer' 9.1.3 运算符与表达式
  • 38.
    5 .运算符的优先级别 SQLServer 中各种运算符的优先顺序如下: ( ) -> ~ -> ^ -> & -> | -> * 、 / 、 % -> + 、 - -> NOT -> AND -> OR 排在前面的运算符的优先级高于其后的运算符。在一个表达式中,先计算优先级较高的运算,后计算优先级低的运算,相同优先级的运算按自左向右的顺序依次进行。 9.1.3 运算符与表达式
  • 39.
    9.2 函 数 9.2.1 常用函数 9.2.2 用户定义函数
  • 40.
    9.2.1 常用函数函数是—组编译好的 Transact-SQL 语句,它们可以带一个或一组数值做参数,也可不带参数,它返回一个数值、数值集合,或执行一些操作。 函数能够重复执行一些操作,从而避免不断重写代码。 SQL Server 2000 支持两种函数类型: (1) 内置函数 : 是一组预定义的函数,是 Transact-SQL 语言的一部分,按 Transact-SQL 参考中定义的方式运行且不能修改。 (2) 用户定义函数 : 由用户定义的 Transact-SQL 函数。它将频繁执行的功能语句块封装到一个命名实体中,该实体可以由 Transact-SQL 语句调用。 9.2 函 数
  • 41.
    1 .字符串函数 字符串函数用来实现对字符型数据的转换、查找、分析等操作,通常用做字符串表达式的一部分。表 9-7 中列出了 SQL Server 的常用字符串函数。 9.2.1 常用函数
  • 42.
    (1) 使用datalength 和 Len 函数 datalength 函数主要用于判断可变长字符串的长度,对于定长字符串将返回该列的长度。要得到字符串的真实长度,通常需要使用 rtrim 函数截去字符串尾部的空格。 Len 函数可以获取字符串的字符个数,而不是字节数,也不包含尾随空格。 9.2.1 常用函数
  • 43.
    例 9-10 从表 department 中读取 manger 列的各记录的实际长度。 SELECT Datalength(rtrim(manger)) AS 'DATALENGTH', Len(rtrim(manger)) AS 'LEN' FROM department 运行结果如下: DATALENGTH LEN 4 2 6 3 6 3 6 3 9.2.1 常用函数
  • 44.
    (2) 使用Soundex 函数 soundex 函数将 char_expr 转换为 4 个字符的声音码,其中第一个码为原字符串的第一个字符,第 2~4 个字符为数字,是该字符串的声音字母所对应的数字,但忽略了除首字母外的串中的所有元音。 Soundex 函数可用来查找声音相似的字符串,但它对数字和汉字均只返回 0 值。 例如 SELECT soundex('1'),soundex('a'), soundex(' 计算机 '), soundex('abc'), soundex ('abcd'), soundex('a12c'), soundex('a 数字 ') 返回值为: 0000 A000 0000 A120 A120 A000 A000 9.2.1 常用函数
  • 45.
    (4) 使用Charindex 函数实现串内搜索 charindex 函数主要用于在串内找出与指定串匹配的串,如果找到的话, charindex 函数返回第一个匹配的位置。格式: Charindex(expr1, expr2[, start_location]) expr1 是待查找的字符串 expr2 是用来搜索 expr1 子串的字符表达式, start_location 是在 expr2 中查找 expr1 的开始位置,如果此值省略、为负或为 0 ,均从起始位置开始查找。 9.2.1 常用函数
  • 46.
    例如 SELECT charindex(',','red,white,blue') 该查询确定了字符串 'red,white,blue' 中第一个逗号的位置。 9.2.1 常用函数
  • 47.
    (5) 使用Patindex 函数 patindex 函数返回在指定表达式中模式第一次出现的起始位置,如果模式没有则返回 0 。格式: Patindex('%pattern%', expression) pattern 是字符串, % 字符必须出现在模式的开头和结尾。 expression 通常是搜索指定子串的表达式或列。 例如: SELECT patindex('%abc%','abc123'), patindex('123','abc123') 子串“ abc” 和“ 123” 在字符串“ abc123” 中出现的起始位置分别为: 1 和 0 。因为子串“ 123” 不是以 % 开头和结尾。 9.2.1 常用函数
  • 48.
    2 .数学函数 数学函数用来实现各种数学运算,如指数运算、对数运算、三角运算等,其操作数为数值型数据,如int 、 float 、 real 、 money 等 表 9-8 列出了 SQL Server 的数学函数。 9.2.1 常用函数
  • 49.
    例 9-11 在同一表达式中使用 sin 、 atan 、 rand 、 pi 、 sign 函数。 SELECT sin(23.45), atan(1.234), rand(), pi(), sign(-2.34) 运行结果如下: -0.99374071017265964 0.88976244895918932 0.1975661765616786 3.1415926535897931 -1.00 9.2.1 常用函数
  • 50.
    例 9-12 用 ceiling 和 floor 函数返回大于或等于指定值的最小整数值和小于或等于指定值的最大整数值。 SELECT ceiling(123),floor(321), ceiling(12.3), ceiling(-32.1),floor(-32.1) 运行结果如下: 123 321 13 -32 -33 9.2.1 常用函数
  • 51.
    SELECT round(12.34512,3), round(12.34567,3),round(12.345,-2), round(54.321,-2) 运行结果如下: 12.34500 12.34600 .000 100.000 Round(numeric_expr, int_expr) 的 int_expr 为负数时,将小数点左边第 int_expr 位四舍五入。 例 9-13 round 函数的使用。 9.2.1 常用函数
  • 52.
    3 .日期函数 日期函数用来操作datetime 和 smalldatetime 类型的数据,执行算术运算。与其他函数一样,可以在 SELECT 语句和 WHERE 子句以及表达式中使用日期函数。 9.2.1 常用函数
  • 53.
    表 9-9 SQL Server 的日期函数 9.2.1 常用函数 返回 date_expr 中的年份值 Year(date_expr) 返回 date_expr 中的月份值 Month(date_expr) 返回 date_expr 中的日期值 Day(date_expr) 返回以 datepart 指定方式表示的 date_expr 加上 number 以后的日期 Dateadd(datepart, number, date_expr) 以 datepart 指定的方式,返回 date_expr2 与 date_expr1 之差 Datediff(datepart,date_expr1,date_expr2) 以整数形式返回 date_expr 中的 datepart 指定部分 Datepart(datepart, date_expr) 以字符串形式返回 date_expr 中的指定部分,如果合适的话还将其转换为名称 ( 如 June) Datename(datepart, date_expr) 返回当前系统的日期和时间 Getdate() 描 述 函数名称及格式
  • 54.
    表 9-10 SQL Server 的日期部分 9.2.1 常用函数 0~999 ms Millisecond 0~59 ss Second 0~59 mi Minute 0~23 hh Hour 1~7(Mon~Sun) dw Weekday 1~54 wk Week 1~31 dd Day 1~366 dy Dayofyear 1~12 mm Month 1~4 qq Quarter 1753~9999 yy Year 取值范围 写 法 日期部分
  • 55.
    例 9-14 使用 datediff 函数来确定货物是否按时送给客户。 SELECT goods_id,datediff(dd,send_date,arrival_date) FROM purchase_order 为了从 datediff 中得到一个正值,应注意把较早的日期放在前面 9.2.1 常用函数
  • 56.
    例 9-15 使用 datename 函数返回员工的出生日期的月份 (mm) 名称。 SELECT employee_name,datename(mm,birth_date) FROM employee 运行结果如下: 钱达理 December 东方牧 April 郭文斌 March 肖海燕 July 张明华 August 9.2.1 常用函数
  • 57.
    4 .系统函数 系统函数用于获取有关计算机系统、用户、数据库和数据库对象的信息。与其他函数一样,可以在SELECT 和 WHERE 子句以及表达式中使用系统函数。 表 9-11 列出了 SQL Server 的系统函数。 9.2.1 常用函数
  • 58.
    例 9-16 使用 object_name 函数返回已知 ID 号的对象名。 SELECT object_name(469576711) 运行结果如下: Employee 9.2.1 常用函数
  • 59.
    例 9-17 利用 object_id 函数,根据表名返回该表的 ID 号。 SELECT name FROM sysindexes WHERE id=object_id('customer') 运行结果如下: name customer _WA_Sys_customer_id_75D7831F 9.2.1 常用函数
  • 60.
    9.2.2 用户定义函数根据函数返回值形式的不同将用户定义函数分为 3 种类型。 (1) 标量函数 标量函数返回一个确定类型的标量值,其函数值类型为 SQL Server 的系统数据类型(除 text 、 ntext 、 image 、 cursor 、 timestamp 、 table 类型外)。函数体语句定义在 BEGIN…END 语句内。 (2) 内嵌表值函数 内嵌表值函数返回的函数值为一个表。内嵌表值函数的函数体不使用 BEGIN…END 语句,其返回的表是 RETURN 子句中的 SELECT 命令查询的结果集,其功能相当于一个参数化的视图。 (3) 多语句表值函数 多语句表值函数可以看作标量函数和内嵌表值函数的结合体。其函数值也是一个表,但函数体也用 BEGIN…END 语句定义,返回值的表中的数据由函数体中的语句插入。 9.2 函 数
  • 61.
    1. 创建用户定义函数(1) 使用 CREATE FUNCTION 创建用户定义函数 标量函数的语法格式: CREATE FUNCTION [owner_name.] function_name ( [{ @parameter_name [AS] scalar_parameter_data_type[=default ] } [ ,...n ] ] ) RETURNS scalar_return_data_type [ WITH < function_option> [ [,] ...n] ] [ AS ] BEGIN function_body RETURN scalar_expression END 注: function_option , ENCRYPTION : 加密函数, SCHEMABINDING : 不能更改或删除相关的数据库对象 9.2.2 用户定义函数
  • 62.
    例 9-18 创建一个用户定义函数 DatetoQuarter ,将输入的日期数据转换为该日期对应的季度值。如输入 '2006-8-5' ,返回 '3Q2006' ,表示 2006 年 3 季度。 CREATE FUNCTION DatetoQuarter(@dqdate datetime) RETURNS char(6) AS BEGIN RETURN(datename(q,@dqdate)+'Q'+datename(yyyy,@dqdate)) END 9.2.2 用户定义函数 例、创建工龄工资计算函数
  • 63.
    调用函数 select emp.dbo.wage(‘1996-1-1’,getdate(),20)as work_year_wage 运行结果如下: 9.2.2 用户定义函数
  • 64.
    内嵌表值函数的语法格式: CREATE FUNCTION[owner_name.] function_name ( [{@parameter_name [AS] scalar_parameter_data_type [=default ] } [ ,...n ] ] ) RETURNS TABLE [ WITH < function_option > [ [,] ...n ] ] [ AS ] RETURN [ ( ] select_stmt [ ) ] 9.2.2 用户定义函数
  • 65.
    例 9-19 创建用户定义函数 goodsq ,返回输入商品编号的商品名称和库存量。 CREATE FUNCTION goodsq(@goods_id varchar(30)) RETURNS TABLE AS RETURN ( SELECT goods_name,stock_quantity FROM goods WHERE goods_id =@goods_id ) 9.2.2 用户定义函数
  • 66.
    例 9-19 创建用户定义函数 goodsq ,返回输入商品编号的商品名称和库存量。 use northwind go CREATE FUNCTION goodsq(@product_id int) RETURNS TABLE AS RETURN(SELECT productid,productname FROM products WHERE productid=@product_id) 9.2.2 用户定义函数
  • 67.
    多语句表值函数的语法格式: CREATE FUNCTION[owner_name.] function_name ( [ { @parameter_name [AS] scalar_parameter_data_type [ = default ] } [ ,...n ] ] ) RETURNS @return_variable TABLE < table_type_definition > [ WITH < function_option > [ [,] ...n ] ] [ AS ] BEGIN function_body RETURN END 9.2.2 用户定义函数
  • 68.
    例 9-20 根据输入的订单编号,返回该订单对应商品的编号、名称、类别编号、类别名称。 CREATE FUNCTION good_info(@in_o_id varchar(10)) RETURNS @goodinfo TABLE (o_id char(6), g_id char(6), g_name varchar(50)) AS BEGIN DECLARE @g_id varchar(10),@g_name varchar(30) SELECT @g_id=goods_id FROM sell_order1 WHERE order_id1=@in_o_id SELECT @g_name=goods_name FROM goods2 WHERE goods_id=@g_id INSERT INTO @goodinfo VALUES(@in_o_id,@g_id,@g_name) RETURN END 9.2.2 用户定义函数
  • 69.
    ① 在企业管理器中选择要创建用户定义函数的数据库(如Sales ),在数据库对象“用户定义函数”项上单击右键,从弹出的快捷菜单中选择“新建用户定义的函数 ...” 选项,出现如图所示的“用户定义函数属性”对话框。 (2) 使用企业管理器创建用户定义函数 9.2.2 用户定义函数
  • 70.
    ② 在“用户定义函数属性”对话框的文本框中指定函数名称(如numtostr ),编写函数的代码。 ③ 单击“检查语法”按钮,出现“语法检查成功”消息框后,单击“确定”按钮,将用户定义函数对象添加到数据库中。 9.2.2 用户定义函数
  • 71.
    2. 执行用户定义函数使用函数需要指出函数所有者,即为函数加上所有者权限作为前缀。其语法格式如下: [database_name.]owner_name.function_name ([argument_expr] [, ...] ) 9.2.2 用户定义函数
  • 72.
    例如,调用例 9-18 创建的用户定义函数DatetoQuarter SELECT dbo.DatetoQuarter ('2006-8-5') 运行结果为: 3Q2006 调用例 9-20 创建的用户定义函数 good_info ,使用以下语句: SELECT * FROM dbo.good_info('S00002') 运行结果为表的记录,如图 9-3 所示。 9.2.2 用户定义函数
  • 73.
    3. 修改和删除用户定义函数用企业管理器修改用户定义函数,选择要修改函数,单击右键,从快捷菜单中选择“属性”选项,打开图 9-2 所示的“用户定义函数属性”对话框。在该对话框中可以修改用户定义函数的函数体、参数等。从快捷菜单中选择“删除”选项,则可删除用户定义函数。 用 ALTER FUNCTION 命令也可以修改用户定义函数。此命令的语法与 CREAT FUNCTION 相同,使用 ALTER FUNCTION 命令相当于重建一个同名的函数。 使用 DROP FUNCTION 命令删除用户定义函数,其语法如下: DROP FUNCTION { [ owner_name .] function_name } [ ,...n ] 其中, function_name 是要删除的用户定义的函数名称。 9.2.2 用户定义函数
  • 74.
    例如,删除例 9-18 创建的用户定义函数DROP FUNCTON DatetoQuarter 删除用户定义函数时,可以不加所有者前缀。 9.2.2 用户定义函数
  • 75.
    9.3 程序控制流语句9.3.1 语句块和注释 9.3.2 选择控制 9.3.3 循环控制 9.3.4 批处理
  • 76.
    9.3.1 语句块和注释Transact-SQL 提供了控制流语言的特殊关键字和用于编写过程性代码的语法结构,可进行顺序、分支、循环、存储过程、触发器等程序设计,编写结构化的模块代码,并放置到数据库服务器上。 9.3 程序控制流语句
  • 77.
    BEGIN...END 用来设定一个语句块,将在 BEGIN...END内的所有语句视为一个逻辑单元执行。 语句块 BEGIN...END 的语法格式为: BEGIN { sql_statement | statement_block } END 1. 语句块 BEGIN...END 9.3.1 语句块和注释
  • 78.
    USE employee GODECLARE @linkman_name char(8) BEGIN SELECT @linkman_name=(SELECT linkman_name FROM customer WHERE customer_id LIKE ‘1001') SELECT @linkman_name END 例 9-21 显示 Sales 数据库中 customer 表的编号为‘ 1001' 的联系人姓名。 9.3.1 语句块和注释
  • 79.
    在 BEGIN...END 中可嵌套另外的BEGIN...END 来定义另一程序块。 例 9-22 语句块嵌套举例。 DECLARE @errorcode int,@nowdate dateTIME BEGIN SET @nowdate=getdate() INSERT sell_order(order_date,send_date,arriver_date,custom_id) VALUES(@nowdate,@nowdate+5,@nowdate+10,'C0002') SELECT @errorcode=@@error IF @errorcode>0 BEGIN RAISERROR(' 当表 sell_order 插入数据时发生错误 !',16,1) RETURN END END 9.3.1 语句块和注释
  • 80.
    2. 注释有两种方法来声明注释: 单行注释 多行注释。 9.3.1 语句块和注释
  • 81.
    ( 1 )单行注释在语句中,使用两个连字符“ --” 开头,则从此开始的整行或者行的一部分就成为了注释,注释在行的末尾结束。 --This is a comment.Whole line will be ignored. SELECT employee_name, address -- 查询所有姓钱的员工 FROM employee WHERE employee_name LIKE ' 钱 %' 注释的部分不会被 SQL Server 执行。 9.3.1 语句块和注释
  • 82.
    ( 2 )多行注释多行注释方法是 SQL Server 自带特性,可以注释大块跨越多行的代码,它必须用一对分隔符“ /* */” 将余下的其他代码分隔开。 /* This is a commnet . All these lines will be ignored. */ /* List all employees.*/ SELECT * FROM employee 注释并没有长度限制。 SQL Server 文档禁止嵌套多行注释,但单行注释可以嵌套在多行注释中。 /* --List all employees. SELECT * FROM employee */ 9.3.1 语句块和注释
  • 83.
    选择结构 选择结构可以使用条件语句来实现。 语法格式:IF < 布尔表达式 > <SQL 语句 >|< 语句块 > [ELSE <SQL 语句 >|< 语句块 >]
  • 84.
    IF (SELECT avg(unit_price)FROM goods WHERE supplier_id='S001')>$9799.0 SELECT 'supplier_id 为 S001 的商品的平均单价比 9799 大 ' ELSE SELECT 'supplier_id 为 S001 的商品的平均单价比 9799 小 ' 运行结果如下: supplier_id 为 S001 的商品的平均单价比 9799 大 例 9-23 判断表 goods 中 supplier_id 为“ S001” 的商品的平均单价是否大于 9799 。 9.3.2 选择控制
  • 85.
    DECLARE @lname varchar(40),@msgvarchar(255) SELECT @lname=' 陈晓兵 ' IF EXISTS(SELECT * FROM department WHERE manager=@lname) BEGIN SELECT @msg=' 有人名为 '+@lname SELECT @msg END ELSE BEGIN SELECT @msg=' 没有人名为 '+@lname SELECT @msg END 运行结果为: 有人名为陈晓兵 例 9-24 用 EXISTS 确定表 department 中是否存在“陈晓兵”。 9.3.2 选择控制
  • 86.
    例 查询学号为1001 的学生。 脚本: if exists(select sno from s where sno='0001') print ' 找到 ' else print ' 未找到 ' 选择结构
  • 87.
    例 查询标识号为’5678’ 的出版商出版的任何书的信息。 脚本: if exists(select * from title where pub_id=‘5678') begin print ‘ 该出版商包含如下书籍:‘ select * from title where pub_id=‘5678’ end else print ' 未找到 ' 选择结构
  • 88.
    例 9-25 嵌套 IF...ELSE 语句的使用。 IF (SELECT SUM(order_num) FROM sell_order)>50 PRINT ' 他们是最佳的客户 ' ELSE IF (SELECT SUM(order_num) FROM sell_order)>30 PRINT ' 必须与他们保持联络 ' ELSE PRINT ' 再想想办法吧 !!‘ 9.3.2 选择控制
  • 89.
    例 查询学号为 1001的学生选修的课程号为 C002 的成绩等级(条件语句的嵌套)。 脚本: 选择结构
  • 90.
  • 91.
    2. CASE 函数CASE 函数计算多个条件并为每个条件返回单个值。 (1) 简单 CASE 函数:将某个表达式与一组简单表达式进行比较以确定结果。 CASE input_expression WHEN when_expression THEN result_expression [ ...n ] [ELSE else_result_expression ] END (2) CASE 搜索函数, CASE 计算一组逻辑表达式以确定结果。 CASE WHEN Boolean_expression THEN result_expression [ ... n ] [ ELSE else_result_expression ] END 9.3.2 选择控制
  • 92.
    例 9-26 使用简单 CASE 函数将 goods 表中的商品分类重命名,以使之更易理解。 SELECT CASE classification_id WHEN 'P001' THEN ' 笔记本计算机 ' WHEN 'P002' THEN ' 激光打印机 ' WHEN 'P003' THEN ' 喷墨打印机 ' WHEN 'P004' THEN ' 交换机 ' ELSE ' 没有这种品牌 ' END AS Classification, goods_name AS 'Goods Name', unit_price AS Price FROM goods WHERE unit_price IS NOT NULL 9.3.2 选择控制
  • 93.
    SELECT goods_name AS 商品名称 , CASE WHEN stock_quantity-order_quantity<=3 THEN ' 紧急进货 ' WHEN stock_quantity-order_quantity>3 and stock_quantity-order_quantity<=10 THEN ' 暂缓进货 ' WHEN stock_quantity-order_quantity>10 THEN ' 货物充足 ' END AS 进货判断 FROM goods 例 9-27 根据 goods 表中库存货物数量与订货量之差,使用 CASE 搜索函数判断该商品是否进货。 9.3.2 选择控制
  • 94.
    上调员工工资,工资级别为 1 的上调8% ,工资级别为 2 的上调 7% ,工资级别为 3 的上调 6% ,其他上调 5% 。 课堂练习:
  • 95.
    循环结构 循环结构可以使用循环语句来实现。 语法格式:WHILE < 布尔表达式 > <SQL 语句 >|< 语句块 > 中断语句: BREAK 短路语句: CONTINUE
  • 96.
    例 9-30 将 goods 表中库存数最大的商品每次订购 2 件,计算如此需要多少次订购才能使库存数不够一次订购。 DECLARE @count int,@maxstockid char(6),@maxstock float SET @count=0 SET @maxstock=(SELECT max(stock_quantity) FROM goods) SET @maxstockid=(SELECT goods_id FROM goods WHERE stock_quantity=@maxstock) SELECT @maxstockid,@maxstock WHILE (@maxstock> (SELECT order_quantity FROM goods WHERE goods_id=@maxstockid)) BEGIN UPDATE goods SET order_quantity=order_quantity+2 WHERE goods_id=@maxstockid SET @count=@count+1 END SELECT @count 运行结果如下: 8 9.3 程序控制流语句
  • 97.
    DECLARE @x int,@yint,@c int SET @x=1,@y=1 WHILE @x<3 BEGIN print @x WHILE @y<3 BEGIN SELECT @c=100*@x+@y print @c SELECT @y=@y+1 END SELECT @x=@x+1 SELECT @y=1 END 9.3 程序控制流语句 运行结果: 1 101 102 2 201 202
  • 98.
    例 9-31 对于 goods 表,如果平均库存少于 12 , WHILE 循环就将各记录库存增加 5% ,再判断最高库存是否少于或等于 25 ,是则 WHILE 循环重新启动并再次将各记录库存增加 5% 。当循环不断地将库存增加直到最高库存超过 25 时,然后退出 WHILE 循环。 /* 执行循环,直到库存平均值超过 12*/ WHILE(SELECT avg(stock_quantity) FROM goods)<12 BEGIN UPDATE goods SET stock_quantity=stock_quantity*1.05 SELECT max(stock_quantity) FROM goods /* 如果最大库存值超过 25 ,则用 BREAK 退出 WHILE 循环,否则继续循环 */ IF(SELECT max(stock_quantity) FROM goods)>25 BEGIN PRINT ' 库存太多了 ' BREAK END ELSE CONTINUE END 9.3 程序控制流语句
  • 99.
    BREAK 语句让程序跳出循环, CONTINUE语句让程序跳过 CONTINUE 命令之后的语句,回到 WHILE 循环的第一行命令,重新开始循环。 BREAK 或 CONTINUE 语句 9.3 程序控制流语句
  • 100.
    DECLARE @s int,@nint,@t int,@c int SET @s=0 SET @n=1 WHILE @n<=10 BEGIN SET @c=1 SET @t=1 WHILE @c<=@n BEGIN SET @t=@t*@c SET @c=@c+1 END SET @s=@s+@t SET @n=@n+1 END SELECT @s,@n 例 9-32 计算 s=1!+2!+…+10! 。 9.3 程序控制流语句
  • 101.
    1 、求 1~10之间的素数和。 脚本: 2 、求 100~200 之间的全部素数。 脚本: 课堂练习:
  • 102.
    转移语句 转移语句将程序的执行流程无条件转移到指定的标号处。 语法格式:GOTO < 标号 > 定义标号时,应在标号名后面加上冒号。 GOTO 语句常用在循环语句和条件语句内,使程序跳出循环或进行分支处理 。
  • 103.
    例 9-11 求 10 的阶乘。 脚本: DECLARE @s int,@times int set @s=1 set @times=1 label1: set @s=@s*@times set @times=@times+1 if @times<=10 goto label1 print ' 结果为 :'+str(@s)
  • 104.
    例 9-28 使用 GOTO 语句改变程序流程。 DECLARE @x int SELECT @x=1 label_1: SELECT @x SELECT @x=@x+1 WHILE ( IF ) @x<6 GOTO label_1 9.3.2 选择控制
  • 105.
    等待语句 等待语句挂起一个程序中语句的执行,直到指定的某一时间点到来或在一定的时间间断之后才继续执行。 语法格式:WAITFOR DELAY '< 时间间隔 >'|TIME '< 时间 >' 其中,时间间隔以及时间均为 datetime 类型,格式为“ hh:mm:ss” ,分别说明等待的时间长度和时间点,在 time 内不能指定日期。
  • 106.
    例 设置等待一小时后执行查询。脚本: begin waitfor delay '1:00:00' select * from s end 例 设置到十点整执行查询。 脚本: begin waitfor time '10:00:00' select * from s end 等待语句
  • 107.
    返回语句 返回语句结束执行,使程序无条件返回,其后面的语句不再执行。 语法格式:RETURN [< 整数表达式 >] 存储过程可以使用 RETURN 语句向调用者返回一个整数值。在 SQL Server 2000 中,存储过程返回值为 0 时,表示存储过程成功执行。
  • 108.
    例 9-29 RETURN 语句应用示例。 DECLARE @x int,@y int SELECT @x=1,@y=2 IF @x>@y RETURN ELSE RETURN 9.3.2 选择控制
  • 109.
    9.3.4 批处理一个批处理是—条或多条 Transact-SQL 语句的集合。 SQL Server 服务器对批处理的处理分为四个阶段: 分析阶段,服务器检查命令的语法,验证表和列的名字的合法性 优化阶段,服务器确定完成一个查询的最有效的方法; 编译阶段,生成该批处理的执行计划; 运行阶段,—条一条地执行该批处理中的语句。 批处理最重要的特征就是它作为一个不可分的实体在服务器上解释和执行。在一些情况下批处理被隐式地设定。例如,用查询分析器来执行一组 Transact-SQL 语句,这组语句将被视为一个批处理来对待。 9.3 程序控制流语句
  • 110.
    SQL Server 有以下几种指定批处理的方法。(1) 应用程序作为一个执行单元发出的所有 SQL 语句构成一个批处理,并生成单个执行计划。 (2) 存储过程或触发器内的所有语句构成一个批处理。每个存储过程或触发器都编译为一个执行计划。 (3) 由 EXECUTE 语句执行的字符串是一个批处理,并编译为一个执行计划。例如, EXEC ('SELECT * FROM employee') (4) 由 sp_executesql 系统存储过程执行的字符串是一个批处理,并编译为一个执行计划。例如, execute sp_executesql N'SELECT * from Sales.dbo.employee' 1. 批处理的指定 9.3 程序控制流语句
  • 111.
    GO 是批处理的结束标志。当编译器执行到 GO时会把 GO 前面的所有语句当成一个批处理来执行。 GO 命令和 Transact-SQL 语句不可处在同一行上。但在 GO 命令行中可以包含注释。 在批处理的第一条语句后执行任何存储过程必须包含 EXECUTE 关键字。局部 ( 用户定义 ) 变量的作用域限制在一个批处理中,不可在 GO 命令后引用。 RETURN 可在任何时候从批处理中退出,而不执行位于 RETURN 之后的语句。 2. 批处理的结束与退出 9.3 程序控制流语句
  • 112.
    USE Sales GO -- 批处理结束标志 CREATE VIEW employee_info AS SELECT * FROM employee GO -- CREATE VIEW 语句与其他语句隔离 SELECT * FROM employee_info GO 例 9-33 创建一个视图,使用 GO 命令将 CREATE VIEW 语句与批处理中的其他语句 ( 如 USE 、 SELECT 语句等 ) 隔离。 9.3 程序控制流语句
  • 113.
    9.4 游标管理与应用9.4.1 游标概述 9.4.2 声明游标 9.4.3 使用游标 9.4.4 游标的应用 9.4.5 使用系统存储过程管理游标
  • 114.
    9.4.1 游标概述1. 游标种类 (1)Transact-SQL 游标:是由 DECLARE CURSOR 语句定义,主要用在服务器上,由从客户端发送给服务器的 Transact-SQL 语句或批处理、存储过程、触发器中的 Transact-SQL 语句进行管理。 Transact-SQL 游标不支持提取数据块或多行数据。 (2)API 游标:支持在 OLE DB 、 ODBC 以及 DB_library 中使用游标函数,主要用在服务器上。每一次客户端应用程序调用 API 游标函数, SQL Server 的 OLE DB 提供者、 ODBC 驱动器或 DB_library 的动态链接库 (DLL) 都会将这些客户请求送给服务器以对 API 游标进行处理。 (3) 客户游标:当客户机缓存结果集时才使用。在客户游标中,有一个默认的结果集被用来在客户机上缓存整个结果集。客户游标仅支持静态游标。一般情况下,服务器游标能支持绝大多数的游标操作,但不支持所有的 Transact-SQL 语句或批处理,所以客户游标常常仅被用作服务器游标的辅助。 由于 API 游标和 Transact-SQL 游标使用在服务器端,所以被称为服务器游标或后台游标,而客户端游标被称为前台游标。 9.4 游标管理与应用
  • 115.
    2. 服务器游标与默认结果集的比较SQL Server 以两种方式为用户返回结果集:默认结果集和服务器游标。 (1) 默认结果集具有的特点:开销小;取数据时提供最大性能;仅支持默认的单进、只读游标功能;返回结果行时一次一行;连接时一次只支持一个活动语句;支持所有 Transact-SQL 语句。 (2) 服务器游标具有的持点:支持所有游标功能;可以为用户返回数据块;在单个连接上支持多个活动语句;以性能补偿游标功能;不支持所有返回多于一行结果集的 Transact-SQL 语句。 使用游标不如使用默认结果集的效率高。在默认结果集中,客户端只向服务器发送要执行的语句。而使用服务器游标时,每个 FETCH 语句都必须从客户端发往服务器,再在服务器中分析此语句并将它编译为执行汁划。 9.4.1 游标概述
  • 116.
    3. 服务器游标与客户端游标的比较使用服务器游标比使用客户游标有以下几方面的优点: (1) 性能:如果要在游标中访问部分数据,使用服务器游标将提供最佳性能,因为只有被取到的数据在网络上发送,客户游标在客户端存取所有结果集。 (2) 更准确的定位更新:服务器游标直接支持定位操作,如 UPDATE 和 DELETE 语句,客户游标通过产生 Transact-SQL 搜索 UPDATE 语句模拟定位游标更新,如果多行与 UPDATE 语句的 WHERE 子句的条件相匹配将导致无意义更新。 (3) 内存使用:使用服务器游标时,客户端不需要高速存取大量数据或者保持有关游标位置的信息,这些都由服务器来完成。 (4) 多活动语句:使用服务器游标时,结果不会存留在游标操作之间的连接上,这就允许同时拥有多个活动的基于游标的语句。 9.4.1 游标概述
  • 117.
    SQL Server 支持4 种类型的服务器游标,它们是单进游标、静态游标、动态游标和键集驱动游标。 (1) 单进游标只支持游标按从前向后顺序提取数据,游标从数据库中提取一条记录并进行操作,操作完毕后,再提取下一条记录。 (2) 静态游标也称为快照游标,它总是按照游标打开时的原样显示结果集,并不反映在数据库中对任何结果集成员所做的修改,因此不能利用静态游标修改基表中的数据。静态游标打开时的结果集存储在数据库 tempdb 中。静态游标始终是只读的。 (3) 动态游标也称为敏感游标,与静态游标相对,当游标在结果集中滚动时,结果集中的数据记录的数据值、顺序和成员的变化均反映到游标上,用户所做的各种操作均可通过游标反映。 (4) 键集驱动游标介于静态游标和动态游标之间,兼有两者的特点。打开键集驱动游标后,游标中的成员和行顺序是固定的。键集驱动游标由一套惟一标识符控制,这些惟一标识符就是键集。用户对基表中的非关键值列插入数据或进行修改造成数据值的变化,在整个游标中都是可见。键集驱动游标的键集在游标打开时建立在数据库 tempdb 中。 4. 服务器游标类型 9.4.1 游标概述
  • 118.
    9.4.2 声明游标SQL Server 游标具有下面的处理过程: (1) 声明游标,定义其特性,如游标中的行是否可以被更新。 (2) 执行 Transact-SQL 语句生成游标。 (3) 在游标中检索要查看的行。从游标中检索一行或几行的操作称为取数据。向前或向后执行取数据操作来检索行的行为称为滚动。 (4) 关闭游标。 9.4 游标管理与应用
  • 119.
    声明客户端游标的步骤 创建一个新的 Visual Basic 项目,添加该 DataGrid 控件,然后添加数据环境。 配置为使用 Microsoft Jet 4.0 OLE DB 提供程序数据环境,并选择数据库文件 Nwind.mdb 。 (数据库文件随 Visual Basic 6.0 和典型的目录是 C:\Program Files \Microsoft Visual Studio\VB98\NWIND.MDB 。 创建新命令,产品,如打开表。 打开新创建的 Command 属性 对话框中,然后单击 高级 选项会确保设置该命令以使用客户端游标和 BatchOptimistic 锁定类型。 配置数据环境和命令的 DataGrid 控件的 DataSource 和 DataMemeber 属性。 运行该项目。 修改第一行,第二列 (ProductName) 的单元格中的值。 单击所的 DataGrid 中的另一个单元格并请注意更改更新到数据库无需调用 UpdateBatch 注意 : 通过重新运行该程序验证更改。 9.4 游标管理与应用
  • 120.
    语法格式如下: DECLARE cursor_name[ INSENSITIVE ] [ SCROLL ] CURSOR FOR select_statement [ FOR { READ ONLY | UPDATE [ OF column_name [ ,...n ] ] } ] 1. SQL-92 游标定义格式 9.4.2 声明游标
  • 121.
    表 9-13 SCROLL 的取值 9.4.2 声明游标 提取游标中的第 n 行数据 ABSULUTE n 提取游标当前位置之前或之后的第 n 行数据 (n 为正表示向后, n 为负表示向前 ) RELATIVE n 提取游标当前位置的下一行数据 NEXT 提取游标当前位置的上一行数据 PRIOR 提取游标中的最后一行数据 LAST 提取游标中的第一行数据 FIRST 含 义 SCROLL 选项
  • 122.
    例 9-34 使用 SQL-92 标准的游标声明语句声明一个游标,用于访问 Sales 数据库中的 goods 表的信息。 USE Sales GO DECLARE Goods_cursor CURSOR FOR SELECT * FROM Goods FOR READ ONLY 9.4.2 声明游标
  • 123.
    语法格式如下: DECLARE cursor_nameCURSOR [ 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 ] ] ] 2. Transact-SQL 扩展游标定义格式 9.4.2 声明游标
  • 124.
    例 9-35 为 customer 表定义一个全局滚动动态游标,用于访问顾客的编号、姓名、地址、电话信息。 DECLARE cur_customer CURSOR GLOBAL SCROLL DYNAMIC FOR SELECT customer_id,customer_name,address,telphone FROM customer 9.4.2 声明游标
  • 125.
    9.4.3 使用游标1. 打开游标 游标声明之后,必须打开才能使用。打开游标的语法格式如下: OPEN {{[GLOBAL] cursor_name} | cursor_variable_name } 例如,打开例 9-35 所声明的游标操作: OPEN cur_customer 9.4 游标管理与应用
  • 126.
    2. 读取游标一旦游标被成功打开,就可以从游标中逐行地读取数据,以进行相关处理。从游标中读取数据主要使用 FETCH 命令。其语法格式为: FETCH [ [NEXT|PRIOR|FIRST|LAST |ABSOLUTE {n|@nvar} |RELATIVE {n|@nvar}] FROM] {{[GLOBAL] cursor_name}| cursor_variable_name } [INTO @variable_name [ ,...n ]] 9.4.3 使用游标
  • 127.
    表 9-14 @@fetch_status 变量 9.4.3 使用游标 所读取的数据已经不存在。 -2 FETCH 命令失败或者行数据己超出了结果集 -1 FETCH 命令已成功执行 0 描 述 返回值
  • 128.
    OPEN cur_customer FETCHNEXT FROM cur_customer /* 取第一个数据行 */ WHILE @@fetch_status = 0 /* 检查 @@fetch_status 是否还有数据可取 */ BEGIN FETCH NEXT FROM cur_customer END 例 9-36 打开例 9-35 中声明的游标,读取游标中的数据。 9.4.3 使用游标
  • 129.
    CLOSE 的语法格式为: CLOSE{{[ GLOBAL ] cursor_name } | cursor_variable_name } 例如,关闭例 9-35 中的游标 cur_customer 的命令: CLOSE cur_customer 游标 cur_customer 在关闭后,仍可用 OPEN 语句打开继续读取数据行。 3. 关闭游标 9.4.3 使用游标
  • 130.
    DEALLOCATE 命令删除游标与游标名或游标变量之间的联系,并且释放游标占用的所有系统资源。其语法格式为: DEALLOCATE{{ [ GLOBAL ] cursor_name } | cursor_variable_name } 一旦某个游标被删除,在重新打开之前,必需再次对其进行声明。 例如,释放例 9-35 所定义的游标 cur_customer 。 DEALLOCATE cur_customer DEALLOCATE cursor_variable_name 语句只删除对游标命名变量的引用。直到批处理、存储过程或触发器结束时变量离开作用域,才释放变量。 4. 释放游标 9.4.3 使用游标
  • 131.
    9.4.4 游标的应用1. 用游标修改和删除表数据 定位修改游标数据的语法格式为: UPDATE table_name SET {column_name={expression | DEFAULT | NULL }[, ... n] WHERE CURRENT OF {{[GLOBAL] cursor_name} | cursor_variable_name} 删除游标数据的语法格式为: DELETE FROM table_name WHERE CURRENT OF {{[GLOBAL] cursor_name} | cursor_variable_name} 9.4 游标管理与应用
  • 132.
    例 9-37 定义游标 cur_customer ,通过 cur_customer 更新 customer 表中的 customer_name 和 linkman_name 列。 DECLARE cur_customer CURSOR FOR SELECT * FROM customer FOR UPDATE OF customer_name,linkman_name /* 该两列可更新 */ OPEN cur_customer /* 打开 cur_customer 游标 */ FETCH NEXT FROM cur_customer /* 将第一行数据放入缓冲区,以便更新操作 */ UPDATE customer SET customer_name=' 南方体育用品公司 ',linkman_name=' 李强 ' WHERE CURRENT OF cur_customer CLOSE cur_customer /* 关闭 cur_customer 游标 */ 9.4.4 游标的应用
  • 133.
    若要删除 customer 表的一行数据,则使用以下命令替换例9-37 中的 UPDATE 语句,就可以删除通过游标读入的一行数据。 DELETE FROM customer WHERE CURRENT OF cur_customer 9.4.4 游标的应用
  • 134.
    2. 使用游标变量CURSOR 关键字还可以作为变量类型来使用,此时,必须要将 CURSOR 进行变量声明。其语法格式为: DECLARE {@cursor_variable_name CURSOR } [ ,...n] 9.4.4 游标的应用
  • 135.
    游标与一个游标变量相关联的方法 (1) 分别定义游标变量与游标,再将游标赋给游标变量。 DECLARE @cur_var CURSOR /* 定义游标变量 */ DECLARE cur_customer CURSOR FOR SELECT * FROM customer /* 定义游标 */ SET @cur_var=cur_customer /* 设置游标与游标变量的关联 */ 9.4.4 游标的应用
  • 136.
    (2) 定义游标变量后,通过SET 命令直接创建游标与游标变量关联 DECLARE @cur_var CURSOR /* 定义游标变量 */ SET @cur_var=CURSOR SCROLL KEYSET FOR SELECT * FROM customer /* 创建游标并与游标变量的关联 */ 9.4.4 游标的应用
  • 137.
    DECLARE @cur_var cursorSET @cur_var =cur_customer OPEN @cur_var FETCH NEXT FROM @cur_var CLOSE @cur_var DEALLOCATE @cur_var 例 9-38 通过游标变量来操作例 9-35 所声明的游标 cur_customer ,操作完成后删除游标变量。 9.4.4 游标的应用
  • 138.
    在游标定义语句中使用了关键字 SCROLL ,则可以用FETCH 语句在游标集合内向前或向后移动,也可以直接跳到集合的某一条记录。 例 9-39 定义可以任意移动的游标。 DECLARE cur_customer CURSOR SCROLL READ_ONLY FOR SELECT * FROM customer 3. 滚动游标 9.4.4 游标的应用
  • 139.
    用 FETCH 语句将记录指针滚动记录OPEN cur_customer FETCH NEXT FROM cur_customer FETCH PRIOR FROM cur_customer FETCH FIRST FROM cur_customer FETCH LAST FROM cur_customer FETCH ABSOLUTE 5 FROM cur_customer FETCH RELATIVE -2 FROM cur_customer 9.4.4 游标的应用
  • 140.
    9.4.5 使用系统存储过程管理游标1. sp_cursor_list sp_cursor_list 显示在当前作用域内的游标及其属性 sp_cursor_list [@cursor_return=]cursor_variable_name OUTPUT, [@cursor_scope=]cursor_scope 9.4 游标管理与应用 表示 LOCAL 、 GLOBAL 游标都返回 3 表示返回所有的 GLOBAL 游标 2 表示返回所有的 LOCAL 游标 1 描 述 cursor_scope 值 表 9-15 游标作用域范围值
  • 141.
    例 9-40 声明一个键值驱动游标,并使用 sp_cursor_list 报告该游标的特性。 DECLARE employee_cur CURSOR KEYSET FOR SELECT employee_name FROM Employee WHERE employee_name LIKE ' 肖 %' OPEN employee_cur DECLARE @report CURSOR /* 声明游标 @report 存储来自 sp_cursor_list 的信息 */ /* 执行 sp_cursor_list 将信息送游标变量 @report*/ EXEC master.dbo.sp_cursor_list @cursor_return = @report OUTPUT,@cursor_scope = 2 FETCH NEXT FROM @report WHILE (@@fetch_status <> -1) BEGIN FETCH NEXT FROM @report END CLOSE @report DEALLOCATE @report GO CLOSE employee_cur DEALLOCATE employee_cur GO 9.4.5 使用系统存储过程管理游标
  • 142.
    sp_describe_cursor 用来显示游标的属性。 sp_describe_cursor[ @cursor_return =] output_cursor_variable OUTPUT { [, [ @cursor_source = ] N'local', [@cursor_identity = ] N'local_cursor_name' ] | [, [ @cursor_source = ] N'global', [ @cursor_identity = ] N'global_cursor_name' ] | [, [ @cursor_source = ] N'variable', [ @cursor_identity = ] N'input_cursor_variable' ] } 2. sp_describe_cursor 9.4.5 使用系统存储过程管理游标
  • 143.
    例 9-41 定义并打开一个全局游标,使用 sp_describe_cursor 报告游标的特性。 DECLARE employee_cur CURSOR STATIC FOR SELECT employee_name FROM employee OPEN employee_cur DECLARE @report CURSOR EXEC master.dbo.sp_describe_cursor @cursor_return = @Report OUTPUT, @cursor_source = N'global', @cursor_identity = N'employee_cur' FETCH NEXT from @report WHILE (@@FETCH_STATUS <> -1) BEGIN FETCH NEXT from @report END CLOSE @report DEALLOCATE @report GO CLOSE employee_cur DEALLOCATE employee_cur GO 9.4.5 使用系统存储过程管理游标
  • 144.
    本章小结 数据类型是学习 Transact-SQL语言的基础,不同种类的数据类型具有不同的精度和取值范围。 (2) 变量分为局部变量和全局变量两种。局部变量由 DECLARE 语句声明,可以由 SET 、 SELECT 或 UPDATE 语句赋值;全局变量不可由用户定义。 (3) Transact-SQL 的运算符分为算术运算符、位运算符、比较运算符、逻辑运算符、连接运算符,每种运算符都有专门的数据类型或操作数,各运算符间遵循一定的优先级。 (4) 函数是—组编译好的 Transact-SQL 语句。 SQL Server 2000 支持的函数分为内置函数和用户定义函数两种类型。用户定义函数可以通过企业管理器和 Transact-SQL 语句来管理。 CREATE FUNCTION 、 ALTER FUNCTION 、 DROP FUNCTION 分别创建、修改、删除用户定义函数。用户定义函数的调用要在函数名前加所有者作为前缀。 (5) 程序控制流语句 BEGIN 和 END 要一起使用,其功能是将语句块括起来。 IF…ELSE 语句根据条件来执行语句块。当程序有多个条件需要判断时,可以使用 CASE 函数实现。 WHILE 循环可根据条件多次重复执行语句。 GOTO 语句会破坏程序结构化的特点,尽量不要使用。 (6) 游标是应用程序通过行来管理数据的一种方法。有 3 种游标: Transact-SQL 游标、 API 服务器游标和客户游标。游标声明使用 DECLARE CURSOR 语句,游标的使用包括打开游标、读取数据、关闭游标、删除游标等,分别使用 OPEN 、 FETCH 、 CLOSE 、 DEARLLOCATE 语句。