求明星大侦探美丽惹的祸12期又是漂亮惹的祸下 百度云资源

SQL Server
第一章基础:数据库构成
数据库中的对象
数据库管理系统中包含许多对象。对于,它常包含以下重要的数据库对象:
数据库索引事务日志程序集表报表文件集全文本目录图表用户自定义数据类型视图角色存储过程用户用户自定义函数
SQL Server的4个系统数据库
在给定的中,数据库实际上是最高层对象。在中,大部分其他对象为数据库对象的子对象。安装好的第一次启动时包含个系统数据库:
主数据库()主数据库保存一组特殊的表(系统表)以用于系统的总体控制。
模型数据库()模型数据库是指可以基于该模型得到一个副本。模型数据库构成新建数据库的模版。也就是说,如果想要改变新建数据库的样式,则可以根据需要更改模型数据库。注意:由于模型数据库作为其他任意数据库的模版,因此系统中必须保留该数据库,禁止删除它。
数据库是代理进程保存任意系统作业的场所,如计划对一数据库在每夜进行备份和执行一次计划好的存储过程。
数据库是服务器主要工作区域之一。只要执行一个复杂或者大型的查询操作,则需要建立一些中间表,而建立的中间表就是在数据库中。只要建立临时表,则这些表会建立在数据库中,即使您是在当前数据库中建立的这些表。只要需要临时保存数据,则很可能是将数据保存在数据库中。数据库与其他任意数据库不同。不仅数据库中的对象是临时的,连数据库本身也是临时的。在每次启动时,数据库会被完全重建。
最基本的对象:表
表由称为域的数据(列)和实体数据(行)构成。数据库中实际的数据都存储在表中。表的定义也包含了描述表中包含数据的类型,即元数据。每一列具有该列可存储数据类型的一组规则。
索引是仅在特定表或视图架构内存在的对象。索引的功能非常类似百科全书中的目录。索引中有以某一特定方式排序的查找值,使用索引可以快速查找数据库中的实际信息。
索引分为两类:
集群索引每一个表只能有一个集群索引。如果是集群索引,其含义为:集群索引对应的表按照其索引进行物理排序。如果为百科全书做索引,则集群索引是书的页码;按页码顺序保存百科全书中的信息。
非集群索引每一个表可以有多个非集群索引。非集群索引的含义与普通索引的含义更接近。如百科全书,非集群索引指的是百科全书后面的关键字目录。
触发器是存在于表框架内的对象。触发器是在表操作时(如进行插入、更新或删除等)自动执行的一段逻辑代码。触发器有多种用途,但主要用于在插入时复制数据或更新时检查数据,确保数据满足相应标准。
约束是仅在表的限制中存在的另一对象。约束就是限制表中数据满足的某种条件。约束在某种方式上类似触发器,尽可能解决数据完整性问题,但他们有所不同,各自具有不同的优点。
数据库中所有的表及其他对象(日志除外)都存储在文件中。这些文件组成了一些所谓的文件组。每个文件组中可以有超过个文件。一个数据库仅能有一个主要文件组,可以有最多个辅助文件组。
视图是一种虚拟表。除了视图本身不包含任意数据外,视图的使用基本与表的使用类似。事实上视图仅仅是存储在表中的数据的映射和表示,它以查询的形式存储在数据库中。应用视图的主要目的是控制用户所要显示的数据。这有两方面的原因:安全和易于使用。
存储过程是编程功能的基础。存储过程通常是逻辑单元中的语句的有序集合。存储过程允许使用变量和参数,也可使用选择和循环结构。与单条语句相比,服务器中使用存储过程有一下几个优点:
不使用长语句字符串而使用短存储过程名,可减少运行存储过程中的代码所要的网络传输。
预先优化和编译,节省存储过程每次运行的时间。
通常考虑安全原因,或仅仅是简化数据库的复杂性,可以将过程封装。
可以调用其他的存储过程,使得它们可以在有限的意义上重用。
但是要注意,存储过程不是函数,它的返回值只能为整数。当存储过程成功执行后会默认返回。完全可以忽略它的返回值,但如果需要根据返回值确定存储过程是否成功执行的话,需要在存储过程的定义中指明返回值。从这点来说,存储过程更像是一个可执行程序,会根据执行情况返回或其他值。
用户自定义函数
用户自定义函数()更符合传统意义上的函数的概念。它和存储过程的不同处有以下几点:
返回值的数据类型包括大部分数据类型。不包括的返回值类型是:、、、及。
基本没有副作用,即用户自定义函数不能完成在其范围之外的功能,比如更改表、发电子邮件或更改系统或数据库参数。
类似于编程语言中使用的函数。函数可以有多个输入变来那个,可以有一个返回值。中,传送到函数的所有变量都是按值传递。还可以返回一种特殊类型的数据类型表。
用户和角色
用户和角色相互并存。用户()等价于登录。简言之,该对象表示登录的标识符。登录的任何人都映射到一个用户。用户属于一个或多个角色()。中可以直接赋予用户或角色执行某操作的权限,一个或多个用户可属于同一角色。
规则和约束()都是限制插入到表中的数据类型的信息。与规则不同的是,约束本身并不是对象,而是描述特定表的多个元数据。规则是为了向下兼容的需要,在新的开发中应该使用约束,避免使用规则。
默认值
中有两种类型的默认值。包括对象本身的默认值,以及表中特定列的元数据的默认值(而非真正对象)。与此非常类似,约束是针对对象,而规则是针对元数据。当插入一条记录时,如果没有提供该列的值,且该列具有其默认值,则自动插入默认值。
用户自定义的数据类型
用户自定义的数据类型是系统定义数据类型的扩展。自版本开始,用户自定义数据类型几乎可定义任意数据。
SQL Server 2005中的数据类型
数据类型名
长度(以字节为单位)
表中的第一个数据类型占个字节;其余个位也用作数据类型。允许空格使其占用一个额外的字节。
可处理日常用到的越来越大的数,其取值范围为。
取值范围为。
取值范围。
取值范围。
Decimal/Numeric&
固定精度,取值范围为。
货币单位,取值范围为,精确到个小数位。注意货币单位可以是任意货币,不限于美元。
SmallMoney&
货币单位,取值范围为。
Float(Real)&
近似小数
由一参数(如)决定其长度与精度。注意参数值表示位数,不是字节数。
日期与时间,取值范围为年月日年月日,精确到秒。
SmallDateTime&
日期与时间,取值范围为年月日年月日,精确到分钟。
指向光标的指针,只占用一个字节,记住组成实际光标的结果集也占内存,占用内存的大小取决于结果集。
Timestamp/rowversion&
特殊小数(二进制)
给定数据库的唯一特定值。即使语句没有列(时间标记),但其值在插入或更新记录的时间自动由数据库设定(不允许直接更新对象)。
UniqueIdentifier&
特殊小数(二进制)
全球唯一标识符(),必须保证在内存空间和时间内唯一。
定长字符数据。比设定长度短时使用空格填充,为非数据,最大长度为字符。
长度可变的字符数据。按需存储,为非数据。允许最大长度为字符,但使用关键字()时表示其长度可足够大(字节)。
保持向后兼容的需要。可使用代替。
定长字符数据。最大长度为字符,比设定长度短时使用空格填充。
长度可变的字符数据。按需存储。允许最大长度为字符,但使用关键字()时表示其长度可足够大(字节)。
保持向后兼容的需要。可使用代替。
定长二进制数,最大长度为字节。
VarBinary&
可变长度二进制数,最大特定长度为字节,可使用关键字()使其作为大对象字段(可达字节)。
保持向后兼容的需要。可使用代替
主要用于结果集,通常作为用户自定义函数返回。在表的定义中不作为可用的数据类型。
SQL_Variant&
用于保存数据类型的容器。当列或函数需要处理多种数据类型时可使用这种数据类型。
定义一字符字段用作数据。提供不符合模式的数据而面向函数的使用的功能。
中没有无符号整数类型。
Server对象标识符
中的所有对象都需要命名,即使在创建时没有指定名称(如表中的约束),也会自动生成一个名称。
中的命名规则非常简单,规则允许名字中内嵌空格,甚至允许名字是关键字。主要的命名规则如下:
对象的名字必须以规范定义的任意字母开头。大小写是否敏感取决于服务器配置的方式。
正常对象的名字最多个字符,临时对象的名字最多个字符。
与关键字相同或包含内嵌空格的名字必须使用双引号(&&)或方括号()。哪一个视为关键字取决于设置数据库的兼容水平。
只有在设置了,双引号才可以作为列名中的分界符。特定的对象类型还存在其他命名规则。
第章语言基础
是的结构化查询语言的&方言&。语言遵守公共语言运行库(),简言之,为语言。可以使用任何语言来访问数据库,而只保留了操作的核心功能。
基本SELECT语句
语句的基本语法规则如下:
SELECT&&column list&
[FROM&&source tables&]
[WHERE&&restrictive condition&]
[GROUP BY&&column name or expression using a column in the SELECT list&]
[HAVING&&restrictive condition based on the GROUP BY results&]
[ORDER BY&&column list& [ASC|DESC]]
WHERE子句中的逻辑运算符
=, &, &, &=, &=, &&, !=, !&, !&&
&Column Name& = &Other Column Name&
&Column Name& = 'Bob'&
标准的比较运算符。要注意大于、小于和等于可能因情况不同而改变。如比较字符串时是否区分大小写。
和都表示不等于,而和分别表示不小于和不大于。
AND, OR, NOT&
&Column1& = &Column2& AND &Column3& &= &Column4&
&Column1& != &MyLiteral& OR &Column2& = &MyOtherLiteral&&
标准的逻辑运算符。运算优先级为、、。
&Column& BETWEEN 1 AND 5&
第一个值在第二个与第三个值之间时其值为,其等价于。
&Column& Like &ROM%&&
可使用和作为通配符。表示可以和任意长度的字符串匹配。表示和任意的单个字符匹配。指定一个字符、字符串或范围,匹配其中的任一个对象。匹配指定字符串以外的任意字符。
&Column& IN (List
of Numbers)
左边的表达式与右边的列表中的任意值匹配时返回。
ALL, ANY, SOME&
&Column|Expression&&比较运算符&&ANY|SOME&(子查询)
子查询中的条件全部任一满足比较运算符时返回。指表达式要匹配结果集中的所有值。和相同,在表达式中匹配结果集中的任一值时返回。
EXISTS (子查询)
子查询返回至少一行记录时为。
常见的统计函数
统计返回的行数(除非使用,否则会忽略值)
计算平均值
计算最小值
计算最大值
DISTINCT和ALL谓词
和均放在的后面。表示去除重复的行,表示保留重复的行。
语句默认是保留重复行的,使用SELECT
DISTINCT &columns...&将返回没有重复的结果集(每行多个字段整体没有重复,而不是单个字段没有重复)。还可应用与统计函数中,表示统计时首先去除重复的行,所以COUNT(DISTINCT
OrderID)将比COUNT(OrderID)返回的行更少。但是在函数中使用没有任何意义。
用于保留重复的行,这是语句的默认设置。但在使用语句时默认会去除重复行,这是可以使用指定保留(SELECT...
UNION ALL SELECT...)。
使用INSERT语句添加数据
语句的语法如下:
INSERT [INTO] &table& [(column_list)]
VALUES (data_values)
在插入中可以使用关键字指定列使用默认值,使用关键字指定列为值。
如果要插入和数据与表的每列一一对应,插入语句可以忽略列名列表可选项。
插入数值类型数据不需要使用引号,而插入字符串或者日期型数据时需要使用引号。
常用的日期型数据格式为以及。
存储过程sp_help
存储过程的功能是给出任意数据库对象、用户定义的数据类型或数据类型的信息。执行存储过程的语法结构如下:
EXEC sp_help &name&
要查看表的属性,只要输入一下命令:
EXEC sp_help sales
INSERT INTO... SELECT语句
INSERT INTO... SELECT语句可完成一次插入一个数据块的功能。其语法结构为语句与语句语法结构的组合,如下:
INSERT INTO &table_name&
[&column list&]
&SELECT statement&
由语句产生的结果集为语句中插入的数据。
用UPDATE语句更改数据
语句的语法结构如下:
UPDATE &table_name&
SET &column& = &value& [,&column& = &value&]
[FROM &source table(s)&]
[WHERE &restrictive condition&]
UPDATE stores
SET city = 'There'
WHERE stor_id = 'TEST'
此外,子句还可以使用表达式:
UPDATE titles
SET price = price * 1.1
WHERE title_id LIKE 'BU%'
DELETE语句
语法结构如下:
DELETE &table_name&
[WHERE &search condition&]
不允许删除作为外键约束引用的行。如果一行使用外键约束引用另一行,则要先删除被引用行后才能删除引用行。
几种形式的JOIN子句
内部连接()
外部连接()
完全连接()
交叉连接()
连接的语法结构
SELECT &select list&
FROM &first_table& &join_type& &second_table&
[ON &join_condition&]
使用关键字(可以省略)给列或者表取别名。同一个查询中的多个表中,可以选择哪些表使用别名,哪些表不使用别名,代码中别名和表名可以混合使用,但是只要确定了使用表的别名,则这个表必须一直使用别名。
内部连接(INNER JOIN)
内部连接根据一个或几个相同的字段将记录匹配在一起,仅仅返回那些匹配的记录。示例:
FROM Products p
INNER JOIN Suppliers s
&&&&ON p.SupplierID = s.SupplierID
外部连接(OUTER JOIN)
外部连接语法结构:
SELECT &select list&
FROM &left table&
&LEFT|RIGHT& [OUTER] JOIN &right table&
ON &join condition&
LEFT OUTER JOIN会使LEFT表中的所有记录都包含到结果集中,即使在RIGHT表中相没有匹配的记录。而RIGHT OUTER JOIN会使RIGHT表中的所有记录都包含到结果集中,即使在LEFT表中相没有匹配的记录。
注意NULL值无法连接NULL值。因为NULL值和NULL值是不相等的。
USE Northwind
SELECT c.CustomerID, CompanyName
FROM Customers c
LEFT JOIN Orders o
&&&&ON c.CustomerID = o.CustomerID
WHERE o.CustomerID IS NULL
完全连接(FULL JOIN)
完全连接用来将两侧的数据完全匹配,并返回所有的记录,无论是记录在的哪一侧表中。完全连接的目的是返回没有参考的记录之间的所有关系,要返回的是连接两侧表的所有记录,而且不丢弃任何记录。
交叉连接(CROSS JOIN)
交叉连接不使用运算符,并将的左侧的所有记录与右侧的所有记录连接。简言之,返回的是两侧的笛卡尔积。
SELECT v.VendorName, a.Address
FROM Vendors v
CROSS JOIN Address a
联合(UNION)
用于将一个特殊的运算符,用于将两个或两个以上的查询产生一个结果集。使用处理查询时,要注意以下关键几点:
所有的查询必须在列表中有相同的列数。即如果在第一个查询中选择了列,则在第二个查询中也要选择列。
返回的结果集的标题仅从第一个查询获得,而忽略其他查询的列标题。
查询中的对应列的数据类型必须隐式一致。
与其他非查询不同,查询的默认返回选项为,而不是。可以在查询中使用关键字(),才能返回重复的行。
USE Northwind
SELECT CompanyName AS Name, Address, City, Region, PostalCode, Country
FROM Customers
SELECT CompanyName AS Name, Address, City, Region, PostalCode, Country
FROM Suppliers
SELECT FirstName + ' ' + LastName AS Name, Address, City, Region, PostalCode, Country
FROM Employees
第章创建和修改数据表
4.1 SQL Server中的对象名
表有层命名约定。完全限定命名如下所示:
[ServerName.[DatabaseName,[SchemaName.]]]ObjectName
4.1.1 模式名称
如果使用模式,那么需要指定对象是在哪种模式下的。不同模式下可以有两个同名的对象。如果想访问不在默认模式下的对象,那么需要特别指明对象的模式名称(即广为人知的所有权)。
1. 关于模式的进一步讨论
在以前的发布中,所有权实际上很像它字面上的意思:即是通过完全限定名称来识别是谁&拥有&这个对象。通常,所有这或者是创建该对象的用户,或者是数据库的所有者(通常指)。一个所有者与特定的登录相关,而模式可以在多个登录之间共享,一个登录也可以拥有多个模式。
在默认情况下,只有当用户或者是系统角色的成员,或者是或数据库角色时,才能在数据库中创建对象。
用户可以被授予一定权限来创建特定类型的数据库及系统对象。如果这些个体用户已经创建了一个对象,那么在默认情况下,这个对象被授予那个登录下默认的模式。
存在一个特征,并不是说就应该使用这个特征。授予权限给普通用户可能会出现不愉快的事情。简单来说,将权限限制在账户或成员或安全角色之内。
2. 默认模式:dbo
无论谁创建了数据库,都被认为是&数据库所有者&,即。在数据库里面创建的任何对象都带有模式,而不是个体的用户名。
另外,(或者角色的成员)总是的别名。即无论是谁实际上拥有数据库,总拥有完全的权限,就好像是一样。而且登录创建的任何对象都显示所有权为。
例如:假如某个数据库的普通用户,被赋予了权限。如果该用户创建了一个名为的表,那么带有所有者限定的对象名称是。注意,由于这时这个表有特定的所有者(),除了之外的其他用户需要提供所有者限定名称才能正确解析这个表的名称()。现在,假如还有一个用户,登录名为。但是是这个数据库的所有者(不仅仅是的成员)。假如使用与同样的语句创建了名为的表,那么带所有者限定名称的表名称是。还有,因为正好是默认的所有者,所以任何用户都可以用来引用该表。注意,数据库角色的成员创建的对象的默认模式不是,这些对象将被赋予特定用户所设定的默认模式。
4.1.2 数据库名称
需要在当前数据库以外的数据库检索数据时,需要使用数据库限定的命名。记住,当前数据库总是默认的数据库,所以,如果只需要当前数据库中的数据,那么不需要在完全限定的名称中包括数据库名称。
4.2 CREATE语句
语句用来创建数据库中的对象。的第一部分看起来总是这样的:
CREATE &object type& &object name&
4.2.1 CREATE DATABASE
CREATE DATABASE &database name&
代码示例:
CREATE DATABASE Accounting
&&&&NAME = 'Accounting',
&&&&FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\ AccountingData.mdf',
&&&&SIZE = 10MB,
&&&&MAXSIZE = 50MB,
&&&&FILEGROWTH = 5MB
&&&&NAME = 'AccountingLog',
&&&&FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\ AccountingLog.ldf',
&&&&SIZE = 5MB,
&&&&MAXSIZE = 25MB,
&&&&FILEGROWTH = 5MB
各选项含义:
用在两个地方:一个定义存储数据的文件的位置,二是定义存储日志的文件的位置。
指定定义的文件的名称,但是只是一个逻辑名称即在内部使用该名称引用该文件。
3. FILENAME
指定文件的物理名称。数据文件的推荐扩展名为,日志文件的推荐扩展名为,附属文件的推荐扩展名为。
指定文件的初始大小。默认情况下,大小的单位是(兆字节),还可以使用、或者。要记住,这个值至少与模型数据库一样大,而且必须是整数,否则将出错。默认的值与模版数据库一样。
5. MAXSIZE
指定文件长度的最大值。默认情况下,大小的单位是。这个选项没有默认值,如果没有提供该选项,则表示不限制最大值。
6. FILEGROWTH
指定当扩张文件时每次的扩张量,可以提供一个值来说明文件每次增加多少字节,或者提供一个百分比,指定文件每次增长的百分比。
指定日志文件。注意日志文件默认的大小是数据文件大小的。其他方面,日志文件和数据库文件的说明参数相同。
4.2.2 创建数据表
创建表的语法如下:
CREATE TABLE [database_name.[owner].]table_name
&&&&&column name& &data type&
&&&&[[DEFAULT &constant expression&]
&&&&&&&&| [IDENTITY [(seed, increment) [NOT FOR REPLICATION]]]]
&&&&[NULL | NOT NULL]
&&&&[&column constraints&]
&&&&&&&&| [&column name& AS &computed column expression&]
&&&&&&&&| [&table constraint&]
&&&&[,...n]
1. 表和列名称
表和列的推荐命名规则:
名称的每个单词,首字母大写,其他字母小写。
名称尽量短,但是要具有描述性。
限制使用缩写,只使用大家都能理解的缩写。例如表示标识、表示数字、表示组织。
当基于其他表来构建表时,需要在新的表名中包含其他父表的名称。
当名称中有两个单词时,不要用任何分隔符。
2. 数据类型
注意没有默认的数据类型
3. DEFAULT
如果要使用默认值,就必须紧跟在数据类型之后给定这个值。
4. IDENTITY
当你设定一个列为标识列时,自动分配一个顺序号给你插入的每个行。注意列和列是完全不同的概念,既不会因为有一个列就说明这个值是唯一的(例如,可以重新设置种子,使用前面用过的值)。值通常用于列,但并不是必须这样使用。
5. NOT FOR REPLICATION
参数决定:当列(通过复制)发布到另外一个数据库时,是否为新的数据库分配一个新的标识值,还是保留已有的值。
6. NULL/NOT NULL
默认的设置是列值是,除非指定允许为空。然而,有很多不同的设置可以改变这个设置,从而影响这个默认值。
列约束就是对单个列设置的关于该列可插入数据的限制和规则。
该列值是由表中其他列动态生成的。具体的语法如下:
&column name& AS &computed column expression&
ExtendedPrice AS Price * Quantity
ListPrice AS Cost * 1.2
相关的限制条件:
不能使用子查询,而且值不能来自其他不同的表。
在之前,不能使用计算列作为键的任何部分,也不能和默认约束一起使用。在中,可以在约束中使用计算列。
以前版本的另外一个问题是在计算列中创建索引的能力。可以在计算列上创建索引,但是必须采用特定的步骤。
表约束和列约束很相似,但表约束可以基于多个列。表层次的约束包括约束、约束以及约束。
表定义中的子句可以指定表位于哪个文件组。大多数时间,可以省略子句,那些表将位于默认文件组中。
11. TEXTIMAGE_ON
该选择将表的特定部分移动到不同的文件组中。这个子句只有在表的定义中有、和列时才有效。当使用子句时,只是将信息移动到分离的文件组中表的其他部分还在默认文件组或者子句选择的文件组中。
12. 创建一个表
USE Accounting
CREATE TABLE Customers
&&&&CustomerNo INT IDENTITY NOT NULL,
&&&&CustomerName VARCHAR(30) NOT NULL,
&&&&Address1 VARCHAR(30) NOT NULL,
&&&&Address2 VARCHAR(30) NOT NULL,
&&&&City VARCHAR(20) NOT NULL,
&&&&State CHAR(2) NOT NULL,
&&&&Zip VARCHAR(10) NOT NULL,
&&&&Contact VARCHAR(25) NOT NULL,
&&&&Phone CHAR(15) NOT NULL,
&&&&FedIDNo VARCHAR(9) NOT NULL,
&&&&DateInSystem SMALLDATETIME NOT NULL
使用存储过程查看表的信息:
EXEC sp_help Customers
4.3 ALTER语句
语句用来更改对象。语句总是有相同的开头:
ALTER &object type& &object name&
4.3.1 ALTER DATEBASE
ALTER DATABASE Accounting
&&&&MODIFY FILE
&&&&&&&&NAME = Accounting,
&&&&&&&&SIZE = 100MB
4.3.2 ALTER TABLE
更经常的情况是改变表的结构。这个可以是增加、删除一列或者改变一列的数据类型等。示例:
ALTER TABLE Employees
&&&&&&&&PreviousEmployer VARCHAR(30) NULL,
&&&&&&&&DataOfBirth DATETIME NULL,
&&&&&&&&LastRaiseDate DATETIME NOT NULL, DEFAULT ''
4.4 DROP语句
语句用来删除对象。
DROP &object type& &object name&[, ...n]
如果需要,可以同时删除两个表:
USE Accounting
DROP TABLE Customers, Employees
删除整个数据库:
DROP DATABASE Accounting
确保数据的完整性不是使用数据的程序的责任,而是数据库本身的责任。将数据完整性的责任移到数据库本身是数据库管理的一次革命。
种不同类型的约束:
参照完整性约束
具体的约束类型:
5.1 约束的类型
5.1.1 域约束
域约束处理一个或多个列,确保一个特定列或一组特定列满足特定的标准。
5.1.2 实体约束
实体约束都是关于每个行的。这种形式的约束并不关心一个整体的列,只对特定的行感兴趣,如约束和约束。
5.1.3 参照完整性约束
参照完整性约束是在某列的值必须与其他列的值匹配时创建的,列可以在同一个表中,或者更通常的是在不同的表中,如约束。
5.2 约束命名
常见的约束的推荐命名规则如下:
CHECK约束以CK开头、主键约束以PK开头、外键约束以FK开头、唯一约束以UN开头。
后接表名、列名。
如在Customers表上对PhoneNo列设置约束:CK_Customers_PhoneNo,Customers表上的主键约束:PK_Custoemrs_CustomerID。
5.3 键约束
常用的键类型:主键、外键、唯一约束。
5.3.1 主键约束
1. 在创建表的时候创建主键约束。
CREATE TABLE Customers
&&&&CustomerNo INT IDENTITY NOT NULL&PRIMARY KEY,
&&&&......
2. 在已存在的表上创建主键约束。
USE Accounting
ALTER TABLE Employees
&&&&ADD CONSTRAINT&PK_EmployeeID
&&&&&&&&PRIMARY KEY (EmployeeID)
5.3.2 外键约束
在语句中设置一列或几列外键约束的语法如下所示:
&column name& &date type& &nullability&
FOREIGN KEY REPERENCES &table name&(&column name&)
&&&&[ON DELETE {CASCADE|NO ACTION|SET NULL|SET DEFAULT}]
&&&&[ON UPDATE {CASCADE|NO ACTION|SET NULL|SET DEFAULT}]
USE Accounting
CREATE TABLE Orders
&&&&OrderID INT IDENTITY NOT NULL
&&&&&&&&PRIMARY KEY,
&&&&CustomerNo INT NOT NULL
&&&&&&&&FOREIGN KEY REFERENCES Customers(CustomerNo),
&&&&OrderDate SMALLDATETIME NOT NULL,
&&&&EmpoyeeID INT NOT NULL
1. 在已存在的表中添加一个外键
ALTER TABLE Orders
&&&&ADD CONSTRAINT&FK_EmployeeCreatesOrder
&&&&&&&&FOREIGN KEY&(EmployeeID)&REFERENCES&Employees(EmployeeID)
2. 使一个表自引用
在实际创建自引用约束之前,很关键的一点是在添加外键之前表中至少有一行。
ALTER TABLE Employees
&&&&ADD CONSTRAINT FK_EmployeeHasManager
&&&&&&&&FOREIGN KEY (ManagerEmpID) REFERENCES Employees(EmployeeID)
3. 级联动作
外键是双向的,即不仅是限制子表的值必须存在于父表中,还在每次对父表操作后检查子行。的默认行为是在子表存在时限制父表不被删除。然而,有时会自动删除任何相关记录,而不是防止删除被引用的记录。同样,在更新记录时,可能希望相关的记录自动引用刚刚更新的记录。这种进行自动删除和更新的过程称为级联。通过修改声明外键的语法添加子句,来定义级联操作。
USE Accounting
CREATE TABLE OrderDetails
&&&&OrderID INT NOT NULL,
&&&&PartNo VARCHAR(10) NOT NULL,
&&&&Description&&&&VARCHAR(25) NOT NULL,
&&&&Qty INT NOT NULL,
&&&&CONSTRAINT PK_OrderDetails
&&&&&&&&PRIMARY KEY (OrderID, PartNo),
&&&&CONSTRAINT FK_OrderContainsDetails
&&&&&&&&FOREIGN KEY (OrderID)
&&&&&&&&&&&&REFERENCES Orders(OrderID)
&&&&&&&&&&&&ON UPDATE NO ACTION
&&&&&&&&&&&&ON DELETE CASCADE
如果对外键定义了,则操作会从父表级联到子表中。即,如果从父表删除了某项,子表中依赖该项的项都会被删除;如果从父表中更新了某项,则子表中依赖该项的字段也会被更新。
值得注意的是:动作所能影响的深度没有限制。
4. 其他操作
为默认操作,即如果子表有依赖,则禁止对父表中的该字段进行删除和更新操作。
操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为,前提是子表中的该项可为值。
操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为在子表中定义的默认值,当然前提是在子表中该字段有默认值。
5.3.3 唯一约束
唯一约束不会自动防止您设置一个值。是否允许值取决于表中相应列的选项的设置。然而,要记住如果您确实允许值,那么只能插入一个。
在创建表时设置唯一约束:
CREATE TABLE Shippers
&&&&ShipperID INT IDENTITY NOT NULL PRIMARY KEY,
&&&&ShipperName VARCHAR(30) NOT NULL,
&&&&Address VARCHAR(30) NOT NULL,
&&&&City VARCHAR(25) NOT NULL,
&&&&State CHAR(2) NOT NULL,
&&&&Zip VARCHAR(10) NOT NULL,
&&&&PhoneNo VARCHAR(14) NOT NULL&UNIQUE
在已存在的表中创建唯一约束:
ALTER TABLE Employees
&&&&ADD CONSTRAINT&AK_EmployeeSSN
&&&&&&&&UNIQUE&(SSN)
在约束名称中的前缀代表&交替键()&,也可以使用前缀或者简单的,代表唯一约束。
5.4 CHECK约束
约束使用与字句一样的规则来定义。约束标准的示例如下:
限制列为合适的数字
BETWEEN 1 AND 12&
合适的格式
LIKE '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]'&
限制的一个特定列表
IN ('UPS', 'Fed Ex', 'USPS')&
价格必须为正
UnitPrice &= 0&
在同一行中引用另外一个列
ShipDate &= OrderDate&
在已存在的表中添加CHECK约束:
ALTER TABLE Customers
&&&&ADD CONSTRAINT&CK_CustomerDateInSystem
&&&&&&&&CHECK&(DateInSystem &= GETDATE())
试着插入违反约束的记录会得到错误。
5.5 DEFAULT约束
约束定义了当插入新行时,在您定义了默认约束的列中没有数据时填充的默认值。要注意:
默认值只在语句中使用在语句和语句中被忽略。
如果在语句中提供了任意的值(包括值),那么就不使用默认值。
如果没有提供值,那么总是使用默认值。
5.5.1 在CREATE TABLE语句中定义DEFAULT约束
CREATE TABLE Shippers
&&&&ShipperID INT IDENTITY NOT NULL
&&&&&&&&PRIMARY KEY,
&&&&ShipperName VARCHAR(30) NOT NULL,
&&&&DataInSystem SMALLDATETIME NOT NULL
&&&&&&&&DEFAULT GETDATE()
5.5.2 在已存在的表中添加DEFAULT约束
ALTER TABLE Customers
&&&&ADD CONSTRAINT&DF_CustomerDefaultDateInSystem
&&&&&&&&DEFAULT GETDATE() FOR&DateInSystem
5.6 使约束失效
5.6.1 在创建约束时忽略无效的数据
默认情况下,除非已存在的数据满足约束标准,否则将不会创建约束。要想在创建约束时不检查已经在表中的数据是否满足约束,可以在添加约束时添加选项。示例:
ALTER TABLE Customers
&&&&WITH NOCHECK
&&&&ADD CONSTRAINT CK_CustomerPhoneNo
&&&&(Phone LIKE '([0-9][0-9][0-9]) [0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]')
5.6.2 临时使已存在的约束失效
使用选项关闭约束,而不是删除它。示例:
ALTER TABLE Customers
&&&&NOCHECK
&&&&CONSTRAINT CK_CustomerPhoneNo
当准备重新让约束起效时,使用选项代替:
ALTER TABLE Customers
&&&&CONSTRAINT CK_CustomerPhoneNo
第章在查询中添加更多内容
6.1 子查询的概念
子查询是嵌套在另外一个查询中的正常的查询。在有一个语句作为部分数据或者另外一个查询的条件的基础时,通过使用括号创建子查询。
子查询通常用于满足下列需求之一:
将一个查询分隔为一系列的逻辑步骤。
提供一个列表作为子句或者、、、、的目标
为父查询中的每个记录提供一个查询表。
6.2 嵌套的子查询
嵌套的子查询只在一个方向嵌套返回在外部查询中使用的单个值,或者在运算符中使用的一个完整的值列表。
在最松散的意义上说,查询语法看起来像下面的两个语法模板:
SELECT &select list&
FROM &some table&
WHERE &some column& = (
&&&&SELECT &single column&
&&&&FROM &some table&
&&&&WHERE &condition that results in only one row returned&)
SELECT &select list&
FROM &some table&
WHERE &some column& IN (
&&&&SELECT &single column&
&&&&FROM &some table&
&&&&WHERE &where condition &)
6.2.1 使用单个值的SELECT语句的嵌套查询
例如,假设希望知道每一天通过系统销售的产品的每个条目的:
SELECT DISTINCK o.OrderDate, od.ProductID
FROM Orders o
INNER JOIN OrderDetails od
&&&&ON o.OrderID = od.OrderID
WHERE o.OrderDate&= (SELECT MIN(OrderDate) FROM Orders)
6.2.2. 使用返回多个值的子查询的嵌套查询
例如,查看所有具有折扣记录的商店列表:
SELECT stor_id AS &Store ID&, stor_name AS &Store Name&
FROM Stores
WHERE stor_id&IN (SELECT stor_id FROM Discounts)
6.2.3. 使用嵌套的SELECT来发现孤立的记录
这种嵌套的和前面示例几乎相同,区别是添加了运算符。这个不同点时的在转化连接语法时设置等于外部连接而不是内部连接。例如,需要查询所有在数据库中没有匹配的折扣记录的商店:
SELECT stor_id AS &Store ID&, stor_name AS &Store Name&
FROM Stores
WHERE stor_id&NOT IN
&&&&(SELECT stor_id FROM Discounts WHERE stor_id IS NOT NULL)
6.3 相互关联的子查询
6.3.1 相互关联的子查询的工作原理
在相互关联的子查询中,内部查询在外部查询提供的信息上运行,反之亦然。有个步骤的处理过程:
外部查询获得一个记录,然后将该记录传递到内部查询。
内部查询根据传递的值执行。
内部查询然后将结果值传回到外部查询,外部查询利用这些值完成处理过程。
6.3.2 在WHERE子句中的相互关联的子查询
例如,需要查询系统中每个顾客第一个订单的和:
SELECT o1.CustomerID, o1.OrderID, o1.OrderDate
FROM Orders o1
WHERE o1.OrderDate = (
&&&&SELECT MIN(o2.OrderDate)
&&&&FROM Orders o2
&&&&WHERE o2.CustomerID = o1.CustomerID)
6.3.3 在SELECT列表中的相互关联的子查询
例如,现在需要查询顾客的姓名和在哪天开始订购商品:
&&&&(SELECT MIN(OrderDate)
&&&&FROM Orders o
&&&&WHERE o.CustomerID = cu.CustomerID) AS &Order Date&
FROM Customers cu
6.3.4 处理NULL数据——ISNULL函数
接受一个变量或者表达式来验证是否是一个空值。如果值确实是,那么函数返回其他预指定的值。如果原来的值不是,那么返回原来的值。语法如下:
ISNULL(&expression to test&, &replacement value if null&)
因此,示例如表所示:
返回值
ISNULL(NULL, 5)&
ISNULL(5, 15)&
ISNULL(MyColumnName, 0) where MyColumnName IS NULL&
ISNULL(MyColumnName, 0) where MyColumnName = 3&
ISNULL(MyColumnName, 0) where MyColumnName = 'Fred Farmer'&
使用示例:
&&&&ISNULL(CAST((SELECT MIN(o.OrderDate)
&&&&&&&&FROM Orders o
&&&&&&&&WHERE o.CustomerID = cu.CustomerID) AS VARCHAR), 'NEVER ORDERED')
&&&&AS &Order Date&
FROM Customers cu
6.3 派生表
例如,现在需要查询既订购了又订购了的所有公司名称。查询代码如下所示:
SELECT panyName
FROM Customers c
INNER JOIN (
&&&&SELECT CustomerID
&&&&FROM Orders o
&&&&INNER JOIN OrderDetails od
&&&&&&&&ON o.OrderID = od.OrderID
&&&&INNER JOIN Products p
&&&&&&&&ON od.ProductID = p.ProductID
&&&&WHERE p.ProductName = 'Chocolade') AS spen
&&&&ON c.CustomerID = spen.CustomerID
INNER JOIN (
&&&&SELECT CustomerID
&&&&FROM Orders o
&&&&INNER JOIN OrderDetails od
&&&&&&&&ON o.OrderID = od.OrderID
&&&&INNER JOIN Products p
&&&&&&&&ON od.ProductID = p.ProductID
&&&&WHERE p.ProductName = 'Vegie-spread') AS spap
&&&&ON c.CustomerID = spap.CustomerID
6.4 EXISTS运算符
使用时,根据是否存在数据满足查询中语句所建立的标准,返回一个简单的和。例如:
SELECT CustomerID, CompanyName
FROM Customers cu
WHERE&EXISTS&(
&&&&SELECT OrderID
&&&&FROM Orders o
&&&&WHERE o.CustomerID = cu.CustomerID)
当使用关键字时,不需要执行一行一行的连接,而是寻找记录,知道找到第一个匹配的记录,停止在那里。只要有一个匹配,就为真,不需要继续查找。
如果需要查询没有订购任何产品的客户,可以使用:
SELECT CustomerID, CompanyName
FROM Customers cu
WHERE&NOT EXISTS&(
&&&&SELECT OrderID
&&&&FROM Orders o
&&&&WHERE o.CustomerID = cu.CustomerID)
6.5 数据类型转换:CAST和CONVERT
和都可以执行数据类型转换。在大部分情况下,两者执行相同的功能,不同的是还提供一些日期格式转换,而没有这个功能。
注意,是兼容的,而不是。
各自的语法如下:
CAST (expression AS data type)
CONVERT (data type, expression[, style])
和可以进行很多数据类型转换,在不进行隐式转换时,需要这种转换。例如:
SELECT 'The Customer has an Order numbered ' + CAST(OrderID AS VARCHAR)
FROM Orders
WHERE CustomerID = 'ALFKI'
例如,需要将列转换为正常数字。一个是个二进制数字,因此需要转换:
SELECT CloTS AS &Uncoverted&, CAST(ColTS AS INT) AS &Converted&
FROM ConvertTest
还可以转换日期:
SELECT OrderDate, CAST(OrderDate AS VARCHAR) AS &Converted&
FROM Orders
WHERE OrderID = 11050
还可以控制日期格式:
SELECT OrderDate,&CONVERT(VARCHAR, OrderDate, 111)&AS &Converted&
FROM Orders
WHERE OrderID = 11050
函数最后一个代码说明需要的格式。注意,任何以超过表示的是位的年份;小于的是两位数字的年份,不过有很少的一些例外,并且小于表示的格式加上后即为对应的位的年份表示的格式。
视图的核心实际上仅仅是一个存储的查询。重要的是可以将来自于基本表(或者其他视图)的数据混合以及匹配,以创建在大多数方面上像另一个基本表那样起作用的对象。
7.1 简单的视图
视图语法的基本形式:
CREATE VIEW &view name&
[WITH ENCRYPTION]
&select statement&
WITH CHECK OPTION
示例创建简单的视图:
USE Accounting
CREATE VIEW CustomerPhoneList_vw
&&&&SELECT CustomerName, Contact, Phone
&&&&FROM Customers
对表创建视图,隐藏隐私信息:
USE Accounting
CREATE VIEW Employees_vw
SELECT EmployeeID,
&&&&FirstName,
&&&&MiddleInitial,
&&&&LastName,
&&&&Title,
&&&&HireDate,
&&&&ManagerEmpID,
&&&&Department
FROM Employees
7.2 作为过滤器的视图
在创建视图时使用字句来过滤查询中的结果。
CREATE VIEW CurrentEmployees_vw
SELECT EmployeeID,
&&&&FirstName,
&&&&MiddleInitial,
&&&&LastName,
&&&&Title,
&&&&HireDate,
&&&&TerminationDate,
&&&&ManagerEmpID,
&&&&Department
FROM Employees
WHERE TerminationDate IS NULL
7.3 更加复杂的视图
在简单视图的基础上添加连接。示例查询订单、零件和消费者信息:
USE Northwind
CREATE VIEW CustomerOrders_vw
&&&&o.OrderID,
&&&&o.OrderDate,
&&&&od.ProductID,
&&&&p.ProductName,
&&&&od.Quantity,
&&&&od.UnitPrice,
&&&&od.Quantity * od.UnitPrice AS ExtendedPrice
FROM Customers AS cu
INNER JOIN Orders AS o
&&&&ON cu.CustomerID = o.CustomerID
INNER JOIN OrderDetails AS od
&&&&ON o.OrderID = od.OrderID
INNER JOIN Products AS p
&&&&ON od.ProductID = p.ProductID
数据库的用户可以方便地查询到消费者的订单信息,而不需要关心四个表的连接:
SELECT CompanyName, ExtendedPrice
FROM CustomerOrders_vw
WHERE OrderDate = ''
7.4 通过视图改变数据
从使用的观点来看,视图非常像表那样地工作。但是,现在来看看一些不同之处。
你可以成功地对视图执行、以及语句。但是,当通过视图改变数据的时候,有一些内容需要注意:
如果视图包含连接,在大多数情况下,除非使用触发器,否则不能对数据执行或者操作。在一些情况下,可以不使用触发器来工作,但是仅限于个别情况。
如果视图仅仅引用单个的表,那么在表中所有需要的字段都在视图中或者有默认值得情况下,可以直接执行操作。否则,需要使用触发器。
在一个有限的范围内,可以限制在视图中可以插入和更新的内容以及不可以插入和更新的内容。
1. 以连接的数据方式处理视图的变化
如果视图包含连接,在大多数情况下,除非使用触发器,否则不能对数据执行或者操作。
2. 必要的字段必须在视图中出现或者具有默认值
3. 约束插入到视图中的内容——WITH CHECK OPTION
的规则很简单为了通过使用视图更新或者插入数据,结果行必须符合要求以出现在视图结果中。即如果通过视图插入的值不能在视图中显示出来,则禁止插入该行记录。
7.5编辑视图
语句会完全替换现有的视图。使用语句和先删除后新建视图的区别在于:
期望找到一个已存在的视图,而则相反。
保留了视图上任何已经建立的权限信息。
保留了任何依赖信息。
7.6 删除视图
DROP VIEW &view name& [, &view name& [...n]]
7.7 保护代码:加密视图
使用选项加密视图,注意和出现的位置不同:
ALTER VIEW CustomerOrders_vw
WITH ENCRYPTION
&&&&o.OrderDate,
&&&&od.ProductID,
&&&&p.ProductName,
&&&&od.Quantity,
&&&&od.UnitPrice,
&&&&od.Quantity * od.UnitPrice AS ExtendedPrice
FROM Customers AS cu
INNER JOIN Orders AS o
&&&&ON cu.CustomerID = o.CustomerID
INNER JOIN OrderDetails AS od
&&&&ON o.OrderID = od.OrderID
INNER JOIN Products AS p
&&&&ON od.ProductID = p.ProductID
注意,一旦源代码被加密了,就没有办法恢复。
第8章 脚本与批处理
8.1 脚本基础
脚本示例:
USE Northwind
DECLARE @Ident INT
INSERT INTO Orders
(CustomerID, OrderDate)
('ALFKI', DATEADD(day, -1, GETDATE()))
SELECT @Ident = @@IDENTITY
INSERT INTO OrderDetails
(OrderID, ProductID, UnitPrice, Quantity)
(@Ident, 1, 50, 25)
SELECT 'The OrderID of the INSERTed row is ' + CONVERT(VARCHAR(8), @Ident)
8.1.1 USE语句
语句用于设置当前数据库。语句会影响在完全限定对象名的数据库部分使用默认值的任何地方。
8.1.2 声明变量
语句具有相当简单的语法:
DECLARE @&variable name& &variable type& [, …]
可以一次仅仅声明一个变量,也可以一次声明几个变量。变量开始的值将总是为,直到显示地将变量设置为一些其他的值。
1. 为变量设置值
目前在变量中设置值的方法有两种。可以使用语句或者语句。从功能上看,它们的作用几乎是相同的,不同的是语句具有使得源值来自语句中的某一列的能力。
使用SET设置变量
通常用于以更加程序化的语言中所使用的方式来设置变量。经典的使用示例是:
SET @TotalCost = 10
SET @TotalCost = @UnitCost * 1.1
使用,不能将查询得到的值赋给变量必须将查询和分开。例如:
USE Northwind
DECLARE @Test MONEY
SET @Test = (SELECT MAX(UnitPrice) FROM OrderDetails)
SELECT @Test
尽管这个语法可以起作用,但习惯上,从来不采用这种方法实现代码。
使用SELECT设置变量
当变量中存储的信息来源于查询的时候,经常用给变量赋值。例如,上面最后的示例中使用是更加常用的做法:
USE Northwind
DECLARE @Test MONEY
SELECT @Test = MAX(UnitPrice) FROM OrderDetails
SELECT @Test
2. 系统函数概述
注意这些系统函数常被人们认为是&系统常量&,但在中更规范的名称为&系统函数&。其中最值得关心的如下所示:
返回当前设置的每个星期的第一天(比如星期日或者星期一)
是一个系统范围的设置如果有人改变了这个设置,就不能得到所期望的结果。
返回在当前连接上的最近的语句错误的数目。如果没有错误,返回
在每个新的语句下重新设置。如果需要保存这个值,应该立刻把这个值移动到一个局部变量中。
返回插入的最近的标识值,作为最近的或者语句的结果
如果没有标识值产生,那么设置为。即使缺少标识值是由于一个运行的语句的失败,也是如此。如果通过一个语句执行多个插入,那么只返回最后的标识值。
仅仅在存储过程中使用。返回称为存储过程的服务器的数值
在希望根据远程服务器不同表现出不同的行为时,这个选项是很方便的。
一个最经常使用的系统函数。返回最近的语句所影响的行的数目。
一般在非运行时错误检查时使用。例如,如果尝试通过使用一个字句删除一行,并且没有行被影响,那么那将意味着一些不期望的事情发生了。
8.1.3 使用@@IDENTITY
是所有的系统函数中最重要的一个。标识列是这样的列,在那里没有提供一个值,而是自动地插入一个值。任何的或者语句都会更新这个函数的返回值。如果没有新的标识列被插入,将返回。如果插入了多个行,生成了多个标识值,则将返回最后生成的标识值。如果语句触发了一个或多个触发器,该触发器又执行了生成标识值的插入操作,那么,在语句执行后立即调用将返回触发器生成的最后一个标识值。如果对包含标识列的表执行插入操作后触发了触发器,并且触发器对另一个没有标识列的表执行了插入操作,则将返回第一次插入的标识值。出现或语句失败或大容量复制失败,或者事务被回滚的情况时,值不会恢复为以前的设置。
8.1.4 使用@@ROWCOUNT
说明上一个语句(、、和等)影响了多少行。示例:
USE Northwind
DECLARE @RowCount INT
SELECT * FROM Categories
SELECT @RowCount = @@ROWCOUNT
PRINT 'The value of @@ROWCOUNT was ' + CAST(@RowCount AS VARCHAR(5))
则最后一行显示:
8.2 批处理
批处理是进入一个逻辑单元的语句组。一个批处理中的所有语句被组合为一个执行计划,因此对所有语句一起进行语法分析,并且必须通过语法验证,否则将没有一个语句会执行。但是,这并不能防止运行时错误的发生。如果发生运行时错误,那么任何在发生运行时错误之前执行的语句将仍然是有效的。简言之,如果一个语句不能通过语法分析,那么不会运行任何语句。如果一个语句在运行时失败,那么产生错误语句之前的所有语句都已经运行了。
可以将一个脚本分开为多个批处理,方法是使用语句。语句:
必须自成一行(只有注释可以在相同的行上)。
使得从脚本或者上一个语句开始的所有语句编译成一个执行计划并发送到服务器,与任何其他批处理无关。
不是命令,而是由各种命令实用程序识别的命令。
代码示例:
USE AdventureWorks
DECLARE @MyVarchar VARCHAR(50) – This DECLARE only lasts for this batch!
SELECT @MyVarchar = 'Honey, I''m home…'
PRINT 'Done with first batch…'
PRINT @MyVarchar – This generates an error since @MyVarchar isn't declared in this batch
PRINT 'Done with second batch'
PRINT 'Done with third batch' – Notice that this still gets executed even after the error
结果如下所示:
8.2.1 批处理中的错误
批处理中的错误分成两类:
运行时错误
如果查询分析器发现一个语法错误,那么批处理的处理过程会立即取消。因为语法检查发生在批处理编译或者执行之前,所以在语法检查期间的失败意味着还没有批处理被执行。
运行时错误的工作方式则不同。因为任何在遇到运行时错误之前执行的语句已经完成了,所以除非是未提交的事务的一部分,否则这些语句所做的任何事情的影响将保留下来。一般而言,运行时错误将终止从错误发生地方到批处理末端的批处理的执行。
8.2.2 什么时候使用批处理
批处理有几个目的,但是所有的批处理具有一个共同点在脚本中当一些事情必须发生在另外一件事之前或者分开发生时,使用批处理。
1. 要求有自己的批处理的语句
有一些命令必须完全是它们自己的批处理的一部分。这些命令包括:
CREATE DEFAULT
CREATE PROCEDUER
CREATE RULE
CREATE TRIGGER
CREATE VIEW
如果你想在一个脚本中将这些语句中的任意一些和其他的语句进行组合,那么需要通过使用语句将它们分开为各自的批处理。
注意,如果一个对象,那么应该将语句放在它自己的批处理中或者至少和其他语句在一个批处理中。
2. 使用批处理建立优先权
使用批处理语句的最可能如果在下一个任务开始之前,需要全部完成上一个任务。例如,在尝试使用新数据库时,需要先完成语句:
CREATE DATABASE Test
CREATE TABLE TestTable
另外,当使用语句显著地修改一个列的类型或者添加列时,直到执行修改任务的批处理已经完成时,才能利用这些变化。
ALTER TABLE TestTable
ADD col3 INT
INSERT INTO TestTable(col1, col2, col3)
VALUES (1, 1, 1)
第9章 存储过程和流控制语句
存储过程()有时也称为。存储过程存储于数据库中而不是在单独的文件中,有输入参数、输出参数以及返回值等。
9.1 创建存储过程:基本语法
在数据库中,创建存储过程和创建其他对象的过程一样,除了它使用的关键字外。存储过程的基本语法如下:
CREATE PROCDUER|PROC &sproc name&
&&&&[&parameter_name&[schema.]&data_type& [VARYING][=&default_value&][OUT [PUT]][,
&&&&[&parameter_name&[schema.]&data_type& [VARYING][=&default_value&][OUT [PUT]][,
&&&&&code&
USE Northwind
CRREATE PROC spShippers
&&&&SELECT * FROM Shippers
执行这个存储过程:
EXEC spShippers
9.2 使用ALTER改变存储过程
当使用编辑存储过程的时候,需要记住的是它完全取代了现存的存储过程。使用和的区别在于:
ALTER PROC期望找到现存的存储过程,而CREATE则不是。
ALTER PROC保留了已经建立的存储过程的任何权限。它在系统对象中保留了相同的对象ID并允许保留依赖关系。
ALTER PROC在其他对象上保留了任何依赖关系的信息,这些对象可以调用修改的存储过程。
如果执行,然后使用,这和使用语句一样,几乎都能得到相同的效果,除了一个很重要的区别如果使用和,则需要完全重新建立权限,权限规定了可以使用以及不能使用存储过程的用户。
9.3 删除存储过程
这个过程非常简单:
DROP PROC|PROCEDURE &sproc name&
9.4 参数化(Parameterization)
声明参数需要以下到部分信息:
默认值
语法如下:
@parameter_name [AS] datatype[= default|NULL] [VARYING] [OUTPUT|OUT]
名称有一个简单的规则集合。首先,它必须以开始。此外,命名规则除了不能有嵌套的空格外,它和的命令规则是相同的。
数据类型可以使用内置的或用户自定义的类型。
声明CURSOR类型参数的时候,必须也使用VARYING和OUTPUT选项。
OUTPUT可以简写为OUT。
USE Northwind
CREATE PROC spInsertShipper
&&&&@CompanyName NVARCHAR(40),
&&&&@Phone NVARCHAR(24)
&&&&INSERT INTO Shippers
&&&&VALUES
&&&&&&&&(@CompanyName, @Phone)
可以使用这个新的存储过程来插入新的数据:
EXEC spInstertShipper 'Speedy Shippers, Inc.', '(503)555-5566'
因为并没有为任何参数提供默认值,所以需要提供两个参数。这意味着为了成功运行该存储工程,则必须提供两个参数。
1. 提供默认值
USE Northwind
CREATE PROC spInsertShipperOptionalPhone
&&&&@CompanyName NVARCHAR(40),
&&&&@Phone NVARCHAR(24)&= NULL
&&&&INSERT INTO Shippers
&&&&VALUES (@CompanyName, @Phone)
重新发出命令,但是使用新的存储过程:
EXEC spInsertShipperOptionalPhone 'Speedy Shippers, Inc'
这次一切顺利,成功插入了新的纪录。
2. 创建输出参数
USE Northwind
CREATE PROC spInsertOrder
&&&&@CustomerID NVARCHAR(5),
&&&&@EmployeeID INT,
&&&&@OrderDate DATETIME = NULL,
&&&&@RequiredDate DATETIME = NULL,
&&&&@ShippedDate DATETIME = NULL,
&&&&@ShipVia INT,
&&&&@Freight MONEY,
&&&&@ShipName NVARCHAR(40) = NULL,
&&&&@ShipAddress NVARCHAR(60) = NULL,
&&&&@ShipCity NVARCHAR(15) = NULL,
&&&&@ShipRegion NVARCHAR(15) = NULL,
&&&&@ShipPostalCode NVARCHAR(10) = NULL,
&&&&@ShipCountry NVARCHAR(15) = NULL,
&&&&@OrderID INT OUTPUT
&&&&INSERT INTO Orders
&&&&VALUES
&&&&&&&&@CustomerID,
&&&&&&&&@EmployeeID,
&&&&&&&&@OrderDate,
&&&&&&&&@RequiredDate,
&&&&&&&&@ShippedDate,
&&&&&&&&@ShipVia,
&&&&&&&&@Freight,
&&&&&&&&@ShipName,
&&&&&&&&@ShipAddress,
&&&&&&&&@ShipCity,
&&&&&&&&@ShipRegion,
&&&&&&&&@ShipPostalCode,
&&&&&&&&@ShipCountry
SELECT @OrderID = @@IDENTITY
执行该存储过程的代码如下:
USE Northwind
DECLARE @MyIdent INT
EXEC spInsertOrder
&&&&@CustomerID = 'ALFKI',
&&&&@EmployeeID = 5,
&&&&@OrderDate = '5/1/1999'
&&&&@ShipVia = 3,
&&&&@Freight = 5.00,
&&&&@OrderID = @MyIdenty OUTPUT
SELECT @MyIdent AS IdentityValue
SELECT OrderID, CustomerID, EmployeeID, OrderDate, ShipName
FROM Orders
WHERE OrderID = @MyIdent
需要注意以下几点:
在存储过程声明中,输出参数需要使用关键字。
调用存储过程的时候也必须使用关键字,才能保证参数被正确的输出。注意如果没有使用关键字,不会产生任何错误,但是此时输出参数的值将是无法保证的。
赋给输出变量的变量不需要和存储过程中的内部参数拥有相同的名称。例如在本例中,内部参数叫做,而传给值的变量叫做。
需要使用(或)关键字来调用存储过程。
9.5 流控制语句
提供了大多数流控制语句的典型的选择,同样也有语句,但是它没有像其他语言中预期的那种流控制级的能力。
9.5.1 IF...ELSE语句
语句的实现方式和语言是接近相同的。基本的语法如下:
IF &Boolean Expression&
&&&&&SQL statement& | BEGIN &code series& END
&&&&&SQL statement& | BEGIN &code series& END]
其中的表达式可以是取布尔值的任意表达式。
不恰当的使用值是个常见的陷阱。例如经常会有如下错误出现:
IF @MyVar = NULL
在大多数系统上(遵循标准)这样的表达式永远都不会为真,并且为绕过所有的值结束。想要判断一个值是否为空应该这样来写:
IF @MyVar IS NULL
不要忘记了不等于任何值甚至是。不要使用&&而要使用&&。
1. ELSE子句
结果返回值为的表达式会被当作从而进入子句。也就是说,如果子句中的语句返回值为或者,则执行子句中的语句。
USE Northwind
ALTER PROC spInsertOrder
&&&&@CustomerID NVARCHAR(5),
&&&&@EmployeeID INT,
&&&&@OrderDate DATETIME = NULL,
&&&&@RequiredDate DATETIME = NULL,
&&&&@ShippedDate DATETIME = NULL,
&&&&@ShipVia INT,
&&&&@Freight MONEY,
&&&&@ShipName NVARCHAR(40) = NULL,
&&&&@ShipAddress NVARCHAR(60) = NULL,
&&&&@ShipCity NVARCHAR(15) = NULL,
&&&&@ShipRegion NVARCHAR(15) = NULL,
&&&&@ShipPostalCode NVARCHAR(10) = NULL,
&&&&@ShipCountry NVARCHAR(15) = NULL,
&&&&@OrderID INT OUTPUT
&&&&DECLARE @InsertedOrderDate SMALLDATETIME
&&&&IF DATEDIFF(dd, @OrderDate, GETDATE()) & 7
&&&&&&&&SELECT @InsertedOrderDate = NULL
&&&&&&&&SELECT @InsertedOrderDate =
&&&&&&&&&&&&CONVERT(DATETIME, CONVERT(VARCHAR, @OrderDate, 112))
&&&&INSERT INTO Orders
&&&&VALUES
&&&&&&&&@CustomerID,
&&&&&&&&@EmployeeID,
&&&&&&&&@OrderDate,
&&&&&&&&@RequiredDate,
&&&&&&&&@ShippedDate,
&&&&&&&&@ShipVia,
&&&&&&&&@Freight,
&&&&&&&&@ShipName,
&&&&&&&&@ShipAddress,
&&&&&&&&@ShipCity,
&&&&&&&&@ShipRegion,
&&&&&&&&@ShipPostalCode,
&&&&&&&&@ShipCountry
&&&&SELECT @OrderID = @@IDENTITY
2. 把代码分组为块
提供了把代码分组为块的方法,可以认为这个块是属于一起的。这个块以语句开始,然后直到语句结束。
现在可以修改订单插入的存储过程如下:
USE Northwind
ALTER PROC spInsertOrder
&&&&@CustomerID NVARCHAR(5),
&&&&@EmployeeID INT,
&&&&@OrderDate DATETIME = NULL,
&&&&@RequiredDate DATETIME = NULL,
&&&&@ShippedDate DATETIME = NULL,
&&&&@ShipVia INT,
&&&&@Freight MONEY,
&&&&@ShipName NVARCHAR(40) = NULL,
&&&&@ShipAddress NVARCHAR(60) = NULL,
&&&&@ShipCity NVARCHAR(15) = NULL,
&&&&@ShipRegion NVARCHAR(15) = NULL,
&&&&@ShipPostalCode NVARCHAR(10) = NULL,
&&&&@ShipCountry NVARCHAR(15) = NULL,
&&&&@OrderID INT OUTPUT
&&&&DECLARE @InsertedOrderDate SMALLDATETIME
&&&&IF DATEDIFF(dd, @OrderDate, GETDATE()) & 7
&&&&&&&&SELECT @InsertedOrderDate = NULL
&&&&&&&&PRINT 'Invalid Order Date'
&&&&&&&&PRINT 'Supplied Order Date was greater than 7 days old.'
&&&&&&&&PRINT 'The value has been reset to NULL'
&&&&&&&&SELECT @InsertedOrderDate =
&&&&&&&&&&&&CONVERT(DATETIME, CONVERT(VARCHAR, @OrderDate, 112))
&&&&&&&&PRINT 'The time of Day in Order Date was truncated'
&&&&INSERT INTO Orders
&&&&VALUES
&&&&&&&&@CustomerID,
&&&&&&&&@EmployeeID,
&&&&&&&&@OrderDate,
&&&&&&&&@RequiredDate,
&&&&&&&&@ShippedDate,
&&&&&&&&@ShipVia,
&&&&&&&&@Freight,
&&&&&&&&@ShipName,
&&&&&&&&@ShipAddress,
&&&&&&&&@ShipCity,
&&&&&&&&@ShipRegion,
&&&&&&&&@ShipPostalCode,
&&&&&&&&@ShipCountry
&&&&SELECT @OrderID = @@IDENTITY
9.5.2 CASE语句
CASE语句在某种程度上与一些编程语言中的一些不同语句是等价的。例如:
C、C++、Delphi中的switch
Visual Basic中的select case
COBOL中的evaluate
在T-SQL中使用CASE语句的一个很大的缺点是:在很多方面,它更像替换运算符而非流控制语句。
编写CASE语句的方式不只一种——可以使用输入表达式或者布尔表达式。第一种方法是使用一个输入表达式来与每个WHEN子句中用到的值进行比较。SQL Server文档把这种方法称为简单CASE:
CASE &input expression&
WHEN &when expression& THEN &result expression&
[ELSE &result expression&]
第二种方法将提供一个表达式,其中每个WHEN子句的值将为TRUE或者FALSE。相关文档把它称为搜索CASE:
WHEN &Boolean expression& THEN &result expression&
[ELSE &result expression&]
可以使用CASE语句最好的方式是把它与SELECT语句放一起使用。
1. 简单CASE
简单CASE使用结果等于布尔值的表达式。示例:
USE Northwind
SELECT TOP 10 OrderID, OrderID % 10 AS 'Last Digit', Position =
CASE OrderID % 10
&&&&WHEN 1 THEN 'First'
&&&&WHEN 2 THEN 'Second'
&&&&WHEN 3 THEN 'Third'
&&&&WHEN 4 THEN 'Fourth'
&&&&ELSE 'Something Else'
FROM Orders
2. 搜索CASE
搜索CASE语句和简单CASE语句非常相同,它只有两个很细微的不同点:
没有输入表达式。
WHEN表达式必须为布尔值。
USE Northwind
SELECT TOP 10 OrderID % 10 AS &Last Digit&, ProductID, &How Close?& =
&&&&&&&&WHEN (OrderID % 10) & 3 THEN 'Ends with less than three'
&&&&&&&&WHEN ProductID = 6 THEN 'ProductID is 6'
&&&&&&&&WHEN ABS(OrderID % 10 - ProductID) &= 1 THEN 'Within 1'
&&&&&&&&ELSE 'More than one apart'
FROM OrderDetails
WHERE ProductID & 10
ORDER BY OrderID DESC
注意SQL Server求值的工作方式:
即使两个条件都为真,但只使用第一个条件。
不需要使用&break&语句,在一个条件满足后自动终止。
可以在条件表达式中混合和匹配正在使用的字段。
只要最后等于布尔值的结果,则可以执行任何表达式。
9.5.3 使用WHILE语句循环
语法如下:
WHILE &boolean expression&
&&&&&sql statement& |
&&&&&statement block&
&&&&[BREAK]
&&&&&sql statement&|&statement block&
&&&&[CONTINUE[
在WHILE语句中必须跟上BEGIN...END,其中包含整个语句块。
9.6 通过返回值确认成功或失败
返回值指示了存储过程的成功或者失败,甚至是成功或失败的范围或属性。
RETURN的工作方式
不管是否提供返回值,程序都会收到一个返回值。SQL Server默认地会在完成存储过程时自动返回0。
为了从存储过程向调用代码返回值,只需要使用RETURN语句:
RETURN [&integer value to return&]
返回值必须是整数。
RETURN语句是无条件地从存储过程中退出的。
USE Northwind
CREATE PROC spTestReturns
&&&&DECLARE @MyMessage VARCHAR(50)
&&&&DECLARE @MyOtherMessage VARCHAR(50)
&&&&SELECT @MyMessage = 'Hi, it''s that line before the RETURN'
&&&&PRINT @MyMessage
&&&&RETURN
&&&&SELECT @MyOtherMessage = 'Sorry, but we won''t get this far'
&&&&PRINT @MyOtherMessage
&&&&RETURN
为了能获取RETURN语句的值,需要在EXEC语句中把值赋给变量。
DECLARE @Return INT
EXEC @Return = spTestReturns
SELECT @Return
直接RETURN会默认返回0,需要返回其他整数可以直接写RETURN &integer&。
第10章 用户自定义函数
用户自定义函数和存储过程非常相似,但它们也有一些行为和能力的区别。
10.1用户自定义函数的定义
用户自定义函数()是有序的语句集合,该语句集合能够预先优化和编译,并且可以作为一个单元来调用。它和存储过程的主要区别在于返回结果的方式。为了能支持多种不同的返回值,比存储过程有更多地限制。
可以在使用存储过程的时候传入参数,也可以以参数的形式得到返回值。存储过程可以返回值,不过该值是为了指示成功或失败的,而非返回数据。
然而,可以在使用的时候传入参数,但是可以不传出任何值。还可以返回标量()值,这个值可以是大部分的数据类型。还可以返回表。
按照返回值的类型,有两种类型:
返回标量的
10.2 返回标量值的UDF
这种类型的和大多数内建的函数一样,会向调用脚本或存储过程返回标量值,例如和函数就会返回标量值。
可以返回除了、和以外的任何中有效的数据类型(包含用户自定义类型)。如果想返回整数,也和存储过程不同的是:
返回值的目的是提供有意义的数据,而不是说明成功或失败。
在查询中可以内联地执行函数,而使用存储过程则不行。
示例返回去掉时分秒的日期:
CREATE FUNCTION&DayOnly(@Date DATETIME)
RETURNS&VARCHAR(12)
&&&&RETURN&CONVERT(VARCHAR(12), @Date, 101)
函数的使用方法如下:
FROM Orders
WHERE DayOnly(OrderDate) =&DayOnly(GETDATE())
在一个中调用另一个:
CREATE FUNCTION AveragePrice()
RETURNS MONEY
WITH SCHEMABINDING
&&&&RETURN (SELECT AVG(Price) FROM Titles)
CREATE FUNCTION PriceDifference(@Price MONEY)
RETURN MONEY
&&&&RETURN @Price –&AveragePrice()
使用可以大大增加查询语句的可读性,并实现了代码重用:
SELECT Title,
&&&&Price,
&&&&AveragePrice()&AS Average,
&&&&PriceDifference(Price)&AS Difference
FROM Titles
WHERE Type = 'popular_comp'
10.3 返回表的UDF
可以对返回的表执行,甚至对结果应用条件。相对简单的函数示例如下:
CREATE FUNCTION&fnAuthorList()
RETURN TABLE
SELECT au_id,
&&&&au_lname + ', ' + au_fname AS au_name
&&&&address AS address1,
&&&&city + ', ' + state + ', ' + zip AS address2
FROM authors
这样的话,使用这个函数就像使用表一样:
FROM fnAuthorList()
使用返回表的比使用视图的好处在于可以在中将条件参数化,而视图不得不包含不想要的数据,然后再通过子句过滤。例如:
CREATE FUNCTION fnSalesCount(@SalesQty BIGINT)
RETURNS TABLE
SELECT au.au_id,
&&&&au.aulname + ', ' + au.au_fname AS au_name,
&&&&au.address AS address1,
&&&&city + ', ' + state + ', ' + zip AS address2,
&&&&SUM(s.qty) AS SalesCount
FROM authors au
INNER JOIN titleauthor ta
&&&&ON au.au_id = ta.au_id
INNER JOIN sales s
&&&&ON ta.title_id = s.title_id
GROUP BY au.au_id,
&&&&au.au_lname + ', ' + au.au_fname,
&&&&au.address,
&&&&au.city + ', ' + au.state + ', ' + zip
HAVING SUM(qty) &&@SalesQty
为了执行该函数,只需要调用它并提供参数:
FROM fnSalesCount(25)
再进一步,如果需要查询每一个销售超过本书以上的作者和出版社的信息,这需要连接返回的表:
SELECT DISTINCT p.pub_name, a.au_name
FROM dbo.fnSalesCount(25) AS a
INNER JOIN titleauthor AS ta
&&&&ON a.au_id = ta.au_id
INNER JOIN titles AS t
&&&&ON ta.title_id = t.title_id
INNER JOIN publishers AS p
&&&&ON t.pub_id = p.pub_id
这里对函数进行了连接,就好像它是表或视图一样。唯一的区别在于可以对它进行参数化。
再进一步,UDF也可以递归调用,并同样存在最深32层的限制。
第11章 事务和锁
事务是关于原子性()的。原子性的概念是指可以把一些东西当作一个单元来看待。
事务要有非常明确的开始和结束点。事实上,在中发出的每一个、、和语句都是隐式事务的一部分。即使只发出一条语句,也会把这条语句当作一个事务要么执行语句中的所有内容,要么什么都不执行。确实,这一条语句默认地将作为事务的长度。
关于事务的操作有:
事务:设置起始点。
事务:使得事务成为数据库中永久的、不可撤回的一部分。
事务:本质上说想要忘记它曾经发生过。
事务:创建一个特有的标记符,从而可以做部分的回滚工作。
11.1.1 BEGIN TRAN
语法如下:
BEGIN TRAN|TRANSACTION [&transaction name&|&@transaction variable&]
11.1.2 COMMIT TRAN
事务的提交是完成事务的终点。的语法类似于:
COMMIT TRAN|TRANSACTION [&transaction name&|&@transaction variable&]
11.1.3 ROLLBACK TRAN
可以回到开始的地方或者其中的任何一个保存点。的语法如下:
ROLLBACK TRAN|TRANSACTION [&transaction name& | &save point name& | &@transaction variable& | &@savepoint variable&]
11.1.4 SAVE TRAN
保存事务从本质上说是创建书签。在建立&书签&之后,可以在回滚中引用它。它的好处是可以回滚到代码中想要的点上。的语法如下:
SAVE TRAN|TRANSACTION [&save point name&|&@savepoint variable&]
关于保存点需要记住的是会清除它们执行后之前保存过的保存点都会消失。
11.2 SQL Server记录日志的工作方式
在数据库的正常操作中,大多数执行的活动都是&记录&在事务日志上,而非直接写入数据库中。检查点是指强制地把数据库现在所使用的脏页写入磁盘的周期性操作。脏页是指日志或数据页,它们在读入到缓存后已经被修改,但是所进行的修改还没有写入到磁盘。
11.2.1 失败和恢复
恢复发生在每次启动的时候。获得数据库文件,并且在最后的检查点以后应用日志中的任何提交的改变。日志中任何没有对应提交的改变都会回滚。
第12章 触发器
一些常见的使用触发器的情况包括:
实施参照完整性,例如数据库或服务器中的参照完整性以及许多复杂的关系类型。
创建审计跟踪。
与约束的功能相似,但是用于表、数据库、甚至是服务器之间。
用自己的语句代替用户的操作语句(通常用于允许复杂语句中的插入操作)。
12.1 触发器的概念
触发器是一种特殊类型的存储过程,响应特定的事件。有两种类型的触发器:数据定义语言()触发器和数据操作语言()触发器。
触发器激活了人们以某些方式(、、等)对数据库结构进行修改的响应。触发器是一些加在特殊表或试图上的代码片段。只要加在触发器上的事件在表中发生,触发器中的代码就会自动地运行。不能显式地调用触发器唯一的做法是执行指派给表所需的操作。触发器也没有参数和返回值,因为都不需要。
在中可以使用种类型的触发器,并可以相互混合和匹配:
注意,有些语句不会激活触发器,比如有与语句相似的删除行的效果,但是不会触发任何触发器。
除了触发器需要加在一个表上外,创建触发器的语法类似于其他语法:
CREATE TRIGGER &trigger name&
&&&&ON [&schema name&.]&table or view name&
&&&&[WITH ENCRYPTION]
&&&&{{{FOR | ALTER} [DELETE] [,] [INSERT} [,] [UPDATE]}}
&&&&&sql statements&
12.1.1 ON子句
对创建触发器的对象进行命名。注意如果触发器的类型是(或)触发器,那么字句的目标就必须是一个表(而不能是视图),视图只接受触发器。
12.1.2 WITH ENCRYPTION子句
加密触发器代码。注意语句不会自动加密,如需加密需要再次指明选项。
12.1 3 FOR|AFTER子句
还需要对激活触发器的定时时间做出选择。虽然可以使用长期接触的触发器(也可以使用关键字来替换),而且这也是人们经常考虑的一种触发器,但是也可以使用触发器。对这两种触发器的选择将影响到是在修改数据之前还是之后来输入触发器。
会将两张表放在一起其中的表保存插入记录的副本,另一张表保存删除的任何记录的副本。使用触发器,创建这两张工作表是发生在检查任何约束之前,而使用触发器,这些表的创建是发生在检查约束之后。使用触发器的重点在于可以在视图中清除任何不确定的插入问题。这也意味着在检查约束之前可以采取运动清除违反约束的情况。
使用或声明的触发器,与触发器最大的区别在于它们是在检查完约束之后建立工作表的。
()子句指明了想要在哪种动作下激活触发器。例如:
FOR INSERT, DELETE
注意之前提到过,使用或子句声明的触发器只能加在表上,而不允许加在视图上。
1. INSERT触发器
每当有人向表中插入全新的数据行的时候,都会执行在代码中通过标记声明的触发器的代码。对于插入的每一行来说,会创建该新行的副本并把它插入到称为的表中,该表只在触发器的作用域内存在。
2. DELETE触发器
每个删除的额记录的副本将插入到成为表中,该表同样只在触发器的作用域内存在。
3. UPDATE触发器
会把每一行当作先删除了现有的记录,并插入了全新的行,所以和表均存在。当然,这两个表会有完全相同数量的数据行。而表中的为改变前的数据,表中为改变后的数据。
12.2 为了数据完整性规则使用触发器
触发器可以完成约束和约束一样的功能,但可以使用约束和约束完成的功能不应该再设置触发器。但触发器还是可以完成更多的功能:
业务规则需要引用单个表中的数据。
业务规则需要检查更新的增量(更新前后的区别)。
需要一个定制的错误信息。
12.2.1 处理来自于其他表的需求
例如,客户支持部门的人员不断发出已经停止供应的产品的订单,应该在订单进入系统之前拒绝这些订单的录入。
CREATE TRIGGER OrderDetailNotDiscontinued
&&&&ON OrderDetails
&&&&FOR INSERT, UPDATE
&&&&IF EXISTS (
&&&&&&&&SELECT 'TRUE'
&&&&&&&&FROM Inserted i
&&&&&&&&INNER JOIN Products p
&&&&&&&&&&&&ON i.ProductID = p.ProductID
&&&&&&&&WHERE p.Discontinued = 1)
&&&&&&&&PAISERROR('Order Item is discontinued. Transaction Failed.', 16, 1)
&&&&&&&&ROLLBACK TRAN
12.2.2 使用触发器来检查更新的增量
例如,的存货部门要求不能发出任何销售某个产品超过其一般库存单位的订单。
CREATE TRIGGER ProductIsRationed
&&&&ON Products
&&&&FOR UPDATE
&&&&IF EXISTS (
&&&&&&&&SELECT 'TRUE'
&&&&&&&&FROM Inserted i
&&&&&&&&INNER JOIN Deleted d
&&&&&&&&&&&&ON i.ProductID = d.ProductID
&&&&&&&&WHERE (d.UnitsInStock – i.UnitsInStock) & d.UnitsInStock / 2
&&&&&&&&&&&&AND d.UnitsInStock – i.UnitsInStock & 0
&&&&&&&&RAISERROR('Cannot reduce stock by more than 50%% at onece.', 16, 1)
&&&&&&&&ROLLBACK TRAN
12.3 可以关闭触发器而不删除它
可以使用语句来打开或关闭触发器,语法如下:
ALTER TABLE &table name&
{ENABLE|DISABLE} TRIGGER {ALL|&trigger name&}
12.4 删除触发器
和删除其他对象一样:
DROP TRIGGER &trigger name&
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:25197次
排名:千里之外
原创:16篇
转载:35篇
(1)(1)(6)(4)(7)(4)(1)(6)(3)(6)(13)(1)

我要回帖

更多关于 明星大侦探第二季 的文章

 

随机推荐