186.元素
1.关于Transact-SQL语言
SQL语句只能按照既定的顺序执行,在执行过程中不能根据某些中间结果有选择地或循环地执行某些语句块,不能像高级程序语言那样进行流程控制。为此,微软公司在SQL语言的基础上添加了流程控制语句,从而得到一种结构化程序设计语言——Transact-SQL。
Transact-SQL即事务SQL,也简写为T-SQL,它是微软公司对关系数据库标准语言SQL进行扩充的结果,是SQL语言的超集。Transact-SQL支持所有的标准SQL语言操作。
作为一种标准的关系数据库语言,SQL几乎可以在所有的关系数据库上使用。但由于Transact-SQL是微软对SQL扩充的结果,所以只有SQL Server支持Transact-SQL,而其他关系数据库(如Access、Oracle等)却不支持Transact-SQL。SQL Server已经在市场中占据了主导地位,特别是随着SQL Server 版本的不断翻新,加上微软公司的强力支撑,SQL Server的主导地位将进一步得到加强。无论是数据库管理员还是数据库应用程序开发人员,要想深入领会和掌握数据库技术,必须认真学习Transact-SQL。除了拥有SQL语言所有的功能外,Transact-SQL还具备对SQL Server数据库独特的管理功能。
2.Transact-SQL元素
1. 标识符
在数据库编程中,要访问任何一个逻辑对象(如变量、过程、触发器等)都需要通过其名称来完成。逻辑对象的名称是利用合法的标识符来表示的,是在创建、定义对象时设置的,此后就可以通过名称来引用逻辑对象。
2. 数据类型
与其他编程语言一样,Transact-SQL语言也有自己的数据类型。数据类型在定义数据对象(如列、变量和参数等)时是必须的。自SQL Server 2008版本开始就新增了XML数据类型,以用于保存XML数据。Transact-SQL语言的其他数据类型与SQL的相同。
3. 函数
SQL Server 2008内置了大量的函数,如时间函数、统计函数、游标函数等,极大地方便程序员的使用。
4. 表达式
表达式是由表示常量、变量、函数等的标识符通过运算符连接而成的、具有实际计算意义的合法字符串。有的表达式不一定含有运算符,实际上单个的常量、变量等都可以视为一个表达式,它们往往不含有运算符。
5. 注释
Transact-SQL语言中有两种注释,一种是行单行注释,一种是多行注释。它们分别用符号“--”(连续的两个减号)和“/* */”来实现。
6. 关键字
关键字也称为保留字,是SQL Server预留作专门用途的一类标识符。例如,ADD、EXCEPT、PERCENT等都是保留关键字。用户定义的标识符不能与保留关键字重复。
3.标识符
标识符有两种类型:常规标识符和分隔标识符。
常规标识符在使用时不需将其分隔开,要符合标识符的格式规则。
这些规则就是,标识符中的首字符必须是英文字母、数字、_(下划线)、@、#或汉字,
首字符后面可以是字母、数字、下划线、@和$等字符,可以包含汉字。
标识符一般不能与SQL Server的关键字重复,
也不应以@@开头(因为系统全局变量的标识符是以@@开头),
不允许嵌入空格或其他特殊字符等。
分隔标识符是指包含在两个单引号(' ') 或者方括号([ ]) 内的字符串,这些字符串中可以包含空格。
4.数据类型
4.1 变量的定义和使用
1. 全局变量
在SQL Server中,全局变量是以@@开头,后跟相应的字符串,如@@VERSION等。如果想查看全局变量的值,可用SELECT语句或print语句来完成。
查看全局变量的值@@VERSION的值,相应的print语句如下:
print @@VERSION;
该语句执行后在笔者机器上输出如下结果:
Microsoft SQL Server 2014 - 12.0.4100.1 (X64)
Apr 20 2015 17:29:27
Copyright (c) Microsoft Corporation
Standard Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)
2. 局部变量
(1)定义局部变量
局部变量是由用户定义的,语法如下:
DECLARE @variable1 data_type[,variable2 data_type, …]
@variable1, @variable2,…为局部变量名,它们必须以单字符“@”开头;data_type为数据类型,它是可以是系统数据类型,也可以是用户定义的数据类型,具体选择什么样的类型要根据实际需要而定。有关数据类型的说明见第4章的相关内容。
【例6.1】定义一个用于存储姓名的局部变量。
DECLARE @name_str varchar(8);
【例6.2】同时定义三个分别用于存储学号、出生日期和平均成绩的局部变量。
DECLARE @no_str varchar(8), @birthday_str smalldatetime, @avgrade_num numeric(3,1);
(2)使用SET对局部变量赋初值
在定义局部变量以后,变量自动被赋以空值(NULL)。如果需要对已经定义的局部变量赋一个初值,可用SET语句来实现,其语法如下:
SET @variable = value;
@variable为局部变量名,value为新赋的值。
【例6.3】对上例定义的三个变量@no_str、@birthday_str和@avgrade_num分别赋初值'20170112'、'2000-2-5'和89.8。
这个赋值操作可以下三个SET语句来完成:
SET @no_str='20170112'; SET @birthday_str='2000-2-5'; SET @avgrade_num = 89.8;
注意,不能同时对多个变量进行赋值,这与同时对多个变量进行定义的情况不同
【例子】下列的SET语句是错误的:
SET @no_str='20170112', @birthday_str='2000-2-5', @avgrade_num = 89.8; -- 错误
(3)使用SELECT对局部变量赋初值
SELECT是查询语句,利用该语句可以将查询的结果赋给相应的局部变量。如果查询返回的结果包含多个值,则将最后一个值赋给局部变量。
使用SELECT对局部变量赋初值的语法格式如下:
SELECT @variable1= value1[, @variable2= value2, …] FROM table_name [WHERE …]
【例6.4】 查询表student,将姓名为“刘洋”的学生的学号、出生日期和平均成绩分别赋给局部变量@no_str、@birthday_str和@avgrade_num。
该赋值操作用SELECT语句来实现则非常方便,其代码如下:
SELECT @no_str = s_no, @birthday_str = s_birthday, @avgrade_num = s_avgrade FROM student WHERE s_name = '刘洋';
【例6.5】先定义局部变量@s_no和@s_avgrade,然后对其赋值,最后利用这两个变量修改数据表student的相关信息。
USE MyDatabase GO -- 定义局部变量 DECLARE @s_no varchar(8), @s_avgrade numeric(3,1); -- 对局部变量赋值 SET @s_no='20170208'; SET @s_avgrade = 95.0; -- 使用局部变量 Update student SET s_avgrade = @s_avgrade WHERE s_no = @s_no;
4.2 Transact-SQL常量
1. 字符串常量
字符串常量是由两个单引号来定义的,是包含在两个单引号内的字符序列。
这些字符包括字母数字字符(a-z、A-Z 和0-9)以及特殊字符,如感叹号(!)、at 符(@) 和数字号(#)等。
默认情况下,SQL Server 2014为字符串常量分配当前数据库的默认排序规则,
但也可以用COLLATE 子句为其指定排序规则。
【例子】下列都是合法字符串常量:
'China' '中国人民共和国'
如果字符串中包含一个嵌入的单引号,则需要在该单引号前再加上一个单引号,表示转义,这样才能定义包含单引号的字符串。
【例子】下列包含单引号的字符串都是合法的:
'AbC''Dd!' -- 表示字符串“AbC'Dd!” 'xx: 20%y%.'
有许多程序员习惯用双引号来定义字符串常量。但在默认情况下,SQL Server不允许使用这样的定义方式。
如果将QUOTED_IDENTIFIER 选项设置为OFF,则SQL Server同时支持运用双引号和单引号来定义字符串。
设置QUOTED_IDENTIFIER的方法用下列语句:
SET QUOTED_IDENTIFIER OFF;
在执行该语句后,QUOTED_IDENTIFIER 被设置为OFF。这时除了单引号以外,还可以用双引号来定义字符串。
【例子】下列定义的字符串都是合法的:
'China' '中国人民共和国' 'AbC''Dd!' -- 表示字符串“AbC'Dd!” 'xx: 20%y%.' "China" "中国人民共和国" "AbC''Dd!" -- 表示字符串“AbC''Dd!” "xx: 20%y%."
注意,当用双引号定义字符串时,如果该字符串中包含单引号,则不能在单引号前再加上另一个单引号,否则将得到另外的一种字符串。
【例子】 'AbC''Dd!'定义的是字符串“AbC'Dd!”,而"AbC''Dd!"定义则是字符串“AbC''Dd!”。
SQL Server将空字符串解释为单个空格。
如果不需要用双引号来定义字符串,则只要将QUOTED_IDENTIFIER恢复为默认值ON即可。需要执行下列语句:
SET QUOTED_IDENTIFIER ON;
SQL Server 2014支持Unicode字符串。Unicode字符串是指按照Unicode标准来存储的字符串。但在形式上与普通字符串相似,不同的是它前面有一个N 标识符(N 代表SQL-92 标准中的区域语言),且前缀N必须是大写字母。
【例子】 'China'是普通的字符串常量,而N'China'则是Unicode字符串常量。
2. 整型常量
整型常量也用得很多,它是不用引号括起来且不包含小数点的数字字符串。
【例子】 2007、-14等都是整型常量。
【例子】下面是定义整型常量及对其赋值的例子:
DECLARE @i integer SET @i = 99;
3. 日期时间常量
日期时间常量通常是用字符串常量来表示,
但前提是字符串常量能够隐式转换为日期时间型数据,
其格式为“yyyy-mm-dd hh:mm:ss.nnn”或“yyyy/mm/dd hh:mm:ss.nnn”,
其中yyyy表示年份,第一个mm表示月份,dd表示月份中的日期,
hh表示小时,第二个mm表示分钟,ss表示秒,nnn表示毫秒。
如果“yyyy-mm-dd”缺省,则日期部分默认为1900年01月01日;
如果“hh:mm:ss.nnn”缺省,则时间部分默认为00时00分00.000秒。
【例子】下面是一些将日期时间型常量赋给日期时间型变量的例子:
DECLARE @dt datetime SET @dt = '2017-01-03 21:55:56.890' --2017年01月03日21时55分56.890秒 SET @dt = '2017/12/03' --2017年01月03日0时0分0秒 SET @dt = '2017-01-03' --2017年01月03日0时0分0秒 SET @dt = '21:55:56.890' --1900年01月01日21时55分56.890秒
4. 二进制常量
二进制常量是用前缀为0x的十六进制数字的字符串来表示,但这些字符串不用需要使用单引号括起。
【例子】下列是将二进制常量赋给二进制变量的例子:
DECLARE @bi binary(50) SET @bi = 0xAE SET @bi = 0x12Ef SET @bi = 0x69048AEFDD010E SET @bi = 0x0
5. 数值型常量
数值型常量包括三种类型:decimal型常量、float型常量和real型常量。
decimal型常量是包含小数点的数字字符串,但这些字符串不需单引号括起来(定点表示)。
【例子】下面是decimal型常量的例子:
3.14159
-1.0
float型常量和real型常量都是使用科学记数法来表示(浮点表示)。
【例子】
101.5E5
-0.5E-2
6. 位常量
位常量使用数字0或1来表示,并且不用单引号括起来。如果使用一个大于1的数字,则该数字将转换为1。
【例子】
DECLARE @b bit SET @b = 0;
7. 货币常量
货币常量是前缀为可选的小数点和可选的货币符号的数字字符串,且不用单引号括起来。SQL Server 2008不强制采用任何种类的分组规则,例如在代表货币的字符串中不允许每隔三个数字用一个逗号隔开。
【例子】 以下是货币常量的例子:
$20000.2 -- 而$20,000.2是错误的货币常量 $200
8. 唯一标识常量
这是指uniqueidentifier类型的常量,它使用字符或二进制字符串格式来指定。
【例子】
'6F9619FF-8B86-D011-B42D-00C04FC964FF’ 0xff19966f868b11d0b42d00c04fc964ff
以上介绍的八种类型的常量主要运用于对变量和字段赋值、构造表达式、构造子句等。在今后的介绍中将进一步领会到它的使用方法。
4.3 SQL语言的数据类型
4.3.1 字符串型
SQL Server 2014中,字符串型数据可以由汉字、英文字母、数字等符号组成。根据编码方式的不同,字符串型又分为Unicode字符串型和非Unicode字符串型。
Unicode字符串型数据是指对所有字符均采用双字(16bit)节统一编码的一类数据;
非Unicode字符串型数据则是指对不同国家或地区采用不同编码长度的一类数据,例如英文字母使用一个字节(8bit)进行编码,汉字则使用两个字节(16bit)进行编码。
SQL Server 2014主要支持的字符串型数据类型如表5.2所示。
4.3.2 数值型
按照不同的精确程度,将数值型数据类型分为两种,一种是精确型,另一种是近似型。
精确型数据是指在计算机中可以精确存储的数据。这种数据类型包括各种整型数据类型、定点型数据类型等,
表5.3列出了SQL Server 2014支持的精确型数据类型。
近似型主要是指浮点型float和real。这种类型的数据在内存中不一定能够精确表示,可能会存在一些微小的误差。
4.3.3 日期时间型
日期时间型,包括两种:datetime和smalldatetiime。日期时间型既可以用于存储时间型数据,也可以用于存储日期型数据。
自从SQL Server 2012开始,新增了4种与日期时间相关的新数据类型:datetime2、dateoffset、date和time。
4.3.4 货币型
货币型是用来存储货币值数据,它固定精确到小数点后四位,相当于numeric(m,n)类型的特例(n=4)。SQL Server支持两种货币型。
4.3.5 二进制型
二进制型数据类型包括三种:binary(n)、varbinary(n)和image。其作用和含义说明如表5.7所示。
4.3.6 其他数据类型
SQL Server 2014还支持以下的数据类型:
sql_variant:一种通用数据类型,存储除了text、ntext、image、timestamp和它自身以外的其他类型的数据,其最大存储量为8000字节。
timestamp:时间戳类型,每次更新时会自动更新该类型的数据。作用跟邮局的邮戳类似,通常用于证明某一项活动(操作)是在某一时刻完成的。
uniqueidentitier:全局唯一标识符(GUID),其值可以从Newsequentialid()函数获得,这个函数返回的值对所有计算机来说是唯一的。
xml:作为一种存储格式,xml类型具有SQL Server中其他类型的所有功能,还可以添加子树、删除子树和更新标量值等,最多存储2GB数据。
table:表类型,用于返回表值函数的结果集,其大小取决于表中的列数和行数。
hierarchyid:层次类型,包含对层次结构中位置的引用,占用空间为1至892字节+2字节的额外开销。
cursor:游标类型,包含对游标的引用,只能用作变量或存储过程参数,不能用于Create Table语句中。
4.3.7 自定义数据类型
根据实际需要,用户可利用已有的标准数据类型来定义自己的数据类型,这种类型称为自定义数据类型。自定义数据类型可由CREATE TYPE语句来定义。
【例子】 用户可以用下列语句创建表示地址的数据类型——address:
CREATE TYPE address FROM varchar(350) NOT NULL;
执行上述语句后,就生成了名为address的数据类型,同时增加了约束条件——NOT NULL。就可以用address去定义字段和变量。该数据类型就是与varchar(350)同等,只不过它增加了一个非空约束条件。
不再需要数据类型address,可以用下列语句将之删除:
DROP TYPE address;
但在删除之前,先删除所有引用该数据类型的数据库对象。
5.函数
5.1 系统内置函数
在Transact-SQL程序中常用的系统内置函数可以分为四种类型:字符串处理函数、聚合函数、时间函数和数学函数。
1. 字符串处理函数
(1)ASCII函数
该函数的语法如下:
ASCII ( character_expression )
character_expression为char或varchar类型的字符串表达式。其作用是以int类型返回字符串表达式character_expression中第一个字符的ASCII值。
【例子】 ASCII ('Abcd')返回65,ASCII ('abcd')返回97等('A'和'a'的ASCII值分别为65和97)。
(2)SUBSTRING函数
该函数的语法如下:
SUBSTRING( expression ,start , length )
该函数的作用是返回给定字符expression中的一个子串,该子串是从位置start开始、长度为length的字符串。其中,expression可以是字符串、二进制字符串、文本、图像、列或包含列的表达式,但不能使用包含聚合函数的表达式,start , length都是整型数据。
【例子】 SU BSTRING('abcdef',2, 4)返回'bcde',SUBSTRING('abcdef',2, 1)返回'b'等。
(3)LEFT函数
该函数的语法如下:
LEFT(character_expression, integer_expression)
其作用是返回字符串character_expression中从左边开始的integer_expression个字符。
【例子】 打印学生的姓氏,可以用下列的语句来实现(不考虑复姓):
SELECT LEFT(s_name,1) FROM student
(4)REPLACE函数
该函数的语法如下:
REPLACE(string_expression1 , string_expression2, string_expression3)
其作用是用第三个表达式string_expression3替换第一个字符串表达式string_expression1中出现的所有第二个指定字符串表达式string_expression2的匹配项,并返回替换后的字符串表达式。
【例子】 REPLACE('abcdefghicde','cd','China')将返回'abChinaefghiChinae'。
2. 聚合函数
(1)COUNT函数
该函数用于返回组中的项数,其语法如下:
COUNT({[[ALL|DISTINCT] expression]|*})
该函数有三种调用形式:
COUNT(*):返回组中的行数,包括NULL值和重复行。
COUNT(ALL expression):对组中的每一行都计算expression并返回非NULL值的个数,式中的ALL可以省略。
COUNT(DISTINCT expression):对组中的每一行都计算expression并返回唯一非空值的个数。
(2)AVG函数
该函数返回组中各值的平均值,NULL被忽略。其语法如下:
AVG( [ ALL | DISTINCT ] expression )
【例子】 求女学生的平均成绩,可以用下列的语句来完成:
SELECT AVG(s_avgrade) FROM student WHERE s_sex = '女'
(3)MAX函数
该函数的语法格式如下:
MAX([ ALL | DISTINCT ] expression )
它返回表达式的最大值
(4)MIN函数
该函数的语法格式如下:
MIN([ ALL | DISTINCT ] expression )
它返回表达式的最小值。
(5)SUM函数
该函数的语法格式如下:
SUM([ ALL | DISTINCT ] expression )
当选择ALL(默认)时,它返回表达式expression中所有值的和;当选择DISTINCT时,它返回仅非重复值的和。NULL值被忽略。
3. 日期时间函数
常用的日期时间函数说明如表6.6所示。
4. 数学函数
数学函数用于对数值型字段和表达式进行处理,常用的函数如表6.7所示。
5. 数据类型转换函数
(1)CAST函数
该函数用于将一种数据类型的表达式显式转换为另一种数据类型的表达式。其语法如下:
CAST(expression AS data_type [(length )])
即将表达式expression的值转换为data_type类型的数据,并返回转化后的数据类型。常使用的类型转换主要包括以下几种:
数值型 <-> 字符串型
字符串型 <-> 日期时间型
【例子】
CAST(10.6496 AS int) -- 将常量10.6496转化int类型数据,结果变为10 CAST('abc' AS varchar(5)) -- 将常量'abc'转化为varchar(5)类型 CAST('100' AS int) -- 将字符串常量'100'转化为数值常量100 CAST(100 AS varchar(5)) -- 将数值常量100转化字符串常量'100' CAST('2017/12/12' AS datetime) -- 将字符串常量'2017/12/12'转化为时间常量12 12 2017 CAST(GETDATE() AS VARCHAR(20)) -- 将当前系统时间转化为字符串常量
查询成绩在70到80(不含80)分之间的学生,可以用下列的语句实现:
SELECT * FROM student WHERE CAST(s_avgrade AS varchar(10)) like '7%'
该语句首先将成绩转换为字符串,然后将70到80之间的分数看作是以7开头的字符串,最后通过利用模糊查询来实现该查询功能。它等价于下列的SELECT语句:
SELECT * FROM student WHERE s_avgrade >= 70 and s_avgrade < 80
(2)CONVERT函数
该函数与CAST函数的功能相似,都是用于将一种数据类型的表达式显式转换为另一种数据类型的表达式,但CONVERT函数的功能更强一些。其语法如下:
CONVERT( data_type [ ( length ) ] , expression [ , style ] )
与CAST函数不同的是,在CONVERT函数中被转换的表达式靠近函数式的右边,而在CONVERT函数中则靠近左边。
【例子】下列语句中使用了CONVERT函数,该语句与上述查询语句等价:
SELECT * FROM student WHERE CONVERT(varchar(10), s_avgrade) like '7%'
6 用户自定义函数
用户自定义函数由CREATE FUNCTION语句来定义,分为以下几种类型。
(1)标量函数的定义和引用
标量函数是指返回值为标量值的函数。其语法格式如下:
CREATE FUNCTION [ schema_name. ] function_name ( [ { @parameter_name [ AS ][ type_schema_name. ] parameter_data_type [ = default ] } [ ,...n ] ] ) RETURNS return_data_type [ WITH <function_option> [ ,...n ] ] [ AS ] BEGIN function_body RETURN scalar_expression END [ ; ] <function_option>::= { [ ENCRYPTION ] | [ SCHEMABINDING ] | [ RETURNS NULL ON NULL INPUT | CALLED ON NULL INPUT ] | [ EXECUTE_AS_Clause ] }
其中:
schema_name为架构名称。
function_name为用户定义的函数名,它必须符合SQL Server标识符的规则,在架构schema_name中是唯一的。
@parameter_name为用户定义的形式参数的名称,可以声明一个或者多个参数,parameter_data_type为参数的类型。
function_body表示函数体,即Transact-SQL语句块,是函数的主体部分。
在<function_option>项中,ENCRYPTION用于指示数据库引擎对包含CREATE FUNCTION语句文本的目录视图列进行加密,以防止将函数作为SQL Server复制的一部分发布;SCHEMABINDING指定将函数绑定到其引用的数据库对象,如果其他架构绑定对象也在引用该函数,此条件将防止对其进行更改。
如果定义了 default 值,则无需指定此参数的值即可执行函数。
EXECUTE AS 子句用于指定用于执行用户定义函数的安全上下文。
【例6.15】 构造一个函数,使之能够根据学号从表SC中计算学生已选课程的平均成绩。
USE MyDatabase; GO IF OBJECT_ID(N'dbo.get_avgrade', N'FN') IS NOT NULL DROP FUNCTION dbo.get_avgrade; -- 如果已存在名为get_avgrade的函数则将其删除 GO CREATE FUNCTION dbo.get_avgrade(@s_no varchar(8)) RETURNS float AS BEGIN DECLARE @value float; SELECT @value = AVG(c_grade) FROM sc WHERE s_no = @s_no RETURN @value; END
对于用户自定义函数,其调用方法与变量的引用方式一样,主要有以下几种调用格式。
①在SELECT或者PRINT语句中调用函数
【例子】 可以用下列语句调用自定义函数get_avgrade:
DECLARE @SS varchar(8); SET @SS = '20170202' SELECT dbo.get_avgrade(@SS) --或者, SELECT dbo.get_avgrade('20170202') --如果仅仅用于打印输出,可以将上述的关键字SELECT改为PRINT,也有同样的效果。
② 利用SET语句执行函数
在SET语句中,函数被当作一个表达式进行计算,然后将返回值赋给指定的变量。
【例子】
DECLARE @V float SET @V = dbo.get_avgrade('20170202')
(2)内联表值函数
内联表值函数返回的结果是一张数据表,而不是一个标量值。其语法如下:
CREATE FUNCTION [ schema_name. ] function_name ( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type [ = default ] } [ ,...n ] ] ) RETURNS TABLE [ WITH <function_option> [ ,...n ] ] [ AS ] RETURN [ ( ] select_stmt [ ) ] [ ; ]
可以看到,该函数返回结果是TABLE类型数据(是一张表),且没有函数主体(BEGIN…END部分),而标量函数返回的是一个标量值,有自己的函数主体。
【例子】 定义内联表值函数。该函数的作用是按学号查询学生的学号、姓名和系别,其输入参数是学号,返回结果是由学号、姓名和系别构成的表。
USE MyDatabase; GO IF OBJECT_ID(N'dbo.get_SND', N'IF') IS NOT NULL DROP FUNCTION dbo.get_SND; GO CREATE FUNCTION dbo.get_SND(@s_no varchar(8)) RETURNS TABLE AS RETURN ( SELECT s_no, s_name, s_dept FROM student WHERE s_no = @s_no );
由于内联表值函数返回结果是一张表,所以对其调用必须按照对表的查询方式进行,其调用方法与标量函数的调用方法完全不同。
【例子】 以下是调用函数get_SND的一条SELECT语句,在此函数get_SND是被当作一张表来使用的:
SELECT * FROM dbo.get_SND('20170203')
内联表值函数返回的结果是一张“数据表”,而其本身并不保存数据。在这个意义上,内联表值函数与第8章介绍的视图是一样的,而且也可以像对视图那样对其进行查询操作。其优点是它可以带参数,而视图不能带参数,但它不具备视图的全部功能。可以这样简要理解:内联表值函数是带参数的“视图”。
(3)多语句表值函数
多语句表值函数返回结果也是一张表,但与内联表值函数不同的是:在内联表值函数中,TABLE返回值是通过单个SELECT语句定义的,内联函数没有关联的返回变量。
在多语句表值函数中,@return_variable是TABLE类型的返回变量,用于存储和汇总应作为函数值返回的行。
多语句表值函数返回结果的原理是,先定义一个表变量,然后通过函数体中的语句实现向该表变量插入有关数据,最后将这个表变量作为结果返回。
多语句表值函数的语法如下:
CREATE FUNCTION [ schema_name. ] function_name ( [ { @parameter_name [ AS ] [ type_schema_name. ] parameter_data_type [ = default ] } [ ,...n ] ] ) RETURNS @return_variable TABLE < table_type_definition > [ WITH <function_option> [ ,...n ] ] [ AS ] BEGIN function_body RETURN END [ ; ]
@return_variable为TABLE类型变量,用于存放函数返回的表。
多语句表值函数与标量函数都有函数的主体部分(function_body),它是由一系列定义函数值的Transact-SQL语句组成。在多语句表值函数中,function_body是一系列用于填充TABLE返回变量@return_variable的Transact-SQL 语句;在标量函数中,function_body是一系列用于计算标量值的Transact-SQL 语句。
【例6.16】 下例是一个多语句表值函数,其作用是按学号查询学生的一些基本信息。这些信息包括学号、姓名、专业、系别和平均成绩,其平均成绩是计算列,它由学生已选修的课程及课程成绩来决定。学生选修课程记录于表SC中。
该函数名为get_stu_info,带一个参数,其定义代码如下:
USE MyDatabase; GO IF OBJECT_ID (N'dbo.get_stu_info', N'TF') IS NOT NULL DROP FUNCTION dbo.get_stu_info; GO CREATE FUNCTION dbo.get_stu_info(@no char(8)) RETURNS @stu_info TABLE -- 定义表变量 ( s_no char(8), s_name char(8), s_speciality varchar(50), s_dept varchar(50), s_avgrade numeric(3,1) ) AS BEGIN INSERT @stu_info -- 插入查询信息 SELECT s_no,s_name,s_speciality,s_dept,s_avgrade=( /*从表SC中生成计算列s_avgrade */ SELECT AVG(c_grade) FROM SC WHERE SC.s_no = student.s_no ) FROM student WHERE s_no=@no RETURN END
多语句表值函数的调用与内联表值函数的调用方法一样。
【例子】要查询学号为'20170201'的学生信息,可以按照下列方式调用函数get_stu_info:
SELECT * FROM dbo.get_stu_info('20170201');
用户自定义函数的删除实际上在上述介绍的例子中已经接触过了,即用DROP FUNCTION来实现对函数的删除。
【例子】 删除函数get_stu_info可以使用下列语句来完成:
DROP FUNCTION dbo.get_stu_info;
6.表达式
6.1 Transact-SQL运算符
运算符是用来指定要在一个或多个表达式中执行操作的一种符号。在SQL Server 2014中,使用的运算符包括算术运算符、逻辑运算符、赋值运算符、字符串串联运算符、按位运算符、一元运算符和比较运算符等。
1. 算术运算符
算术运算符加(+)、减(-)、乘(*)、除(/)和取模(%)等五种运算符。它们用于执行对两个表达式的运算,这两个表达式的返回值必须是数值数据类型,包括货币型。
加(+) 和减(-) 运算符还可以用于对日期时间类型值的算术运算。
2. 逻辑运算符
逻辑运算符用于对某些条件进行测试,返回值为TRUE或FALSE。逻辑运算符包括ALL、AND、ANY、BETWEEN、EXISTS、IN、LIKE、NOT、OR、SOME等,其含义说明如表6.2所示,其中有部分运算符已在第5章中介绍过。
3. 赋值运算符
赋值运算符就是等号“=”,它是Transact-SQL中唯一的赋值运算符。例如,上节对局部变量的赋值操作实际上已经使用了赋值运算符。
除了用作赋值操作以外,赋值运算符还可以用于建立字段标题和定义字段值的表达式之间的关系。
【例子】 下列语句创建了两个字段,其中第一个字段的列标题为“中国”,所有字段值均为“China”;第二个字段的列标题为“姓名”,该字段的字段值来自表student中的s_name字段值。
SELECT 中国= 'China', 姓名= s_name FROM student
执行结果如下:
中国 姓名
--------------------------
China 刘洋
China 王晓珂
China 王伟志
China 岳志强
China 贾簿
China 李思思
China 蒙恬
China 张宇
4. 字符串连接运算符
在SQL Server中,字符串连接运算符为加号“+”,表示要将两个字符串连接起来而形成一个新的字符串。该运算符可以操作的字符串类型包括char、varchar、text以及nchar、nvarchar、ntext等。
【例子】
'abc'+'defg' -- 结果为'abcdefg' ‘abc’ + ‘’ + ‘def’ -- 结果为‘abcdef’(默认),当兼容级别设置为65时结果为 'abc def'
针对字符串的操作有很多种,如取子串等。但在SQL Server中仅有字符串连接操作由运算符“+”来完成,而所有其他的字符串操作都使用字符串函数来进行处理。
5. 位运算符
位运算符是表示在两个操作数之间执行按位进行运算的符号,操作数必须为整型数据类型之一,如bit、tinyint、smallint、int、bigint等,还可以是二进制数据类型(image 数据类型除外)。表6.3列出了位运算符及其含义。
6. 比较运算符
比较运算符用于测试两个表达式的值之间的关系,这种关系是指等于、大于、小于、大于等于、小于等于、不等于、不小于、不大于等。比较运算符几乎适用于所有的表达式(除了text、ntext 或image 数据类型的表达式外)。表6.4列出了Transact-SQL 支持的比较运算符。
7. 运算符的优先级
运算符执行顺序的不同会导致不同的运算结果,所以正确地理解运算符的优先级是必要的。图6.1给出了运算符优优先级的示意图,其中从上到下运算符的优先级是由高到低,同一级中运算符的优先级是按照它们在表达式中的顺序从左到右依次降低。
7.注释
注释是Transact-SQL程序代码中不被执行的文本部分,其作用是说明程序各模块的功能和设计思想,以方便程序的修改和维护。
注释有两种方法,一种是用“--”(紧连的两个减号)来注释,另一种是用“/**/”来注释,它们都称为注释符。其中:
• --:用于注释一行代码,被注释的部分是从注释符“--”开始一直到其所在行末尾的部分。
• /* */:用于注释多行代码,被注释的部分包含在两个星号的中间。
【例子】 下面一段代码中同时使用了这两种注释:
USE MyDatabase; -- 使用数据库MyDatabase GO /* 该程序用于查询成绩及格的学生信息,包括学生姓名、性别、平均成绩。 程序编写者:xxx 程序编写时间:2017年12月31日 */ SELECT s_name, s_sex, s_avgrade --姓名、性别、平均成绩 FROM student --在表中查询 GO