《SQL Server 2005 编程入门经典》第一到十二章

 

第一章 RDBMS基础:SQL Server数据库构成

数据库中的对象

数据库管理系统中包含许多对象。对于SQL Server,它常包含以下重要的数据库对象:

数据库索引事务日志程序集报表文件集全文本目录图表用户自定义数据类型视图角色存储过程用户用户自定义函数

 

SQL Server的4个系统数据库

在给定的SQL Server中,数据库实际上是最高层对象。在SQL Server中,大部分其他对象为数据库对象的子对象。安装好的SQL Server第一次启动时包含4个系统数据库:

  • 主数据库(master——主数据库保存一组特殊的表(系统表)以用于系统的总体控制。
  • 模型数据库(model——模型数据库是指可以基于该模型得到一个副本。模型数据库构成新建数据库的模版。也就是说,如果想要改变新建数据库的样式,则可以根据需要更改模型数据库。注意:由于模型数据库作为其他任意数据库的模版,因此系统中必须保留该数据库,禁止删除它。
  • msdb——msdb数据库是SQL代理进程保存任意系统作业的场所,如计划对一数据库在每夜进行备份和执行一次计划好的存储过程。
  • tempdb——tempdb数据库是服务器主要工作区域之一。只要执行一个复杂或者大型的查询操作,则SQL Server需要建立一些中间表,而建立的中间表就是在tempdb数据库中。只要建立临时表,则这些表会建立在tempdb数据库中,即使您是在当前数据库中建立的这些表。只要需要临时保存数据,则很可能是将数据保存在tempdb数据库中。tempdb数据库与其他任意数据库不同。不仅数据库中的对象是临时的,连数据库本身也是临时的。在每次SQL Server启动时,tempdb数据库会被完全重建。

最基本的对象:表

表由称为域的数据(列)和实体数据(行)构成。数据库中实际的数据都存储在表中。表的定义也包含了描述表中包含数据的类型,即元数据。每一列具有该列可存储数据类型的一组规则。

索引

索引是仅在特定表或视图架构内存在的对象。索引的功能非常类似百科全书中的目录。索引中有以某一特定方式排序的查找值,使用索引可以快速查找数据库中的实际信息。

索引分为两类:

  • 集群索引——每一个表只能有一个集群索引。如果是集群索引,其含义为:集群索引对应的表按照其索引进行物理排序。如果为百科全书做索引,则集群索引是书的页码;按页码顺序保存百科全书中的信息。
  • 非集群索引——每一个表可以有多个非集群索引。非集群索引的含义与普通"索引"的含义更接近。如百科全书,非集群索引指的是百科全书后面的关键字目录。

触发器

触发器是存在于表框架内的对象。触发器是在表操作时(如进行插入、更新或删除等)自动执行的一段逻辑代码。触发器有多种用途,但主要用于在插入时复制数据或更新时检查数据,确保数据满足相应标准。

约束

约束是仅在表的限制中存在的另一对象。约束就是限制表中数据满足的某种条件。约束在某种方式上类似触发器,尽可能解决数据完整性问题,但他们有所不同,各自具有不同的优点。

文件组

数据库中所有的表及其他对象(日志除外)都存储在文件中。这些文件组成了一些所谓的文件组。每个文件组中可以有超过32000个文件。一个数据库仅能有一个主要文件组,可以有最多255个辅助文件组。

视图

视图是一种虚拟表。除了视图本身不包含任意数据外,视图的使用基本与表的使用类似。事实上视图仅仅是存储在表中的数据的映射和表示,它以查询的形式存储在数据库中。应用视图的主要目的是控制用户所要显示的数据。这有两方面的原因:安全和易于使用。

存储过程

存储过程是SQL Server编程功能的基础。存储过程通常是逻辑单元中的Transact-SQL语句的有序集合。存储过程允许使用变量和参数,也可使用选择和循环结构。与单条语句相比,服务器中使用存储过程有一下几个优点:

  • 不使用长SQL语句字符串而使用短存储过程名,可减少运行存储过程中的代码所要的网络传输。
  • 预先优化和编译,节省存储过程每次运行的时间。
  • 通常考虑安全原因,或仅仅是简化数据库的复杂性,可以将过程封装。
  • 可以调用其他的存储过程,使得它们可以在有限的意义上重用。

但是要注意,存储过程不是函数,它的返回值只能为整数。当存储过程成功执行后会默认返回0。完全可以忽略它的返回值,但如果需要根据返回值确定存储过程是否成功执行的话,需要在存储过程的定义中指明返回值。从这点来说,存储过程更像是一个可执行程序,会根据执行情况返回0或其他值。

用户自定义函数

用户自定义函数(UDF)更符合传统意义上的函数的概念。它和存储过程的不同处有以下几点:

  • 返回值的数据类型包括大部分SQL Server数据类型。不包括的返回值类型是:textntextimagecursortimestamp
  • 基本没有"副作用",即用户自定义函数不能完成在其范围之外的功能,比如更改表、发电子邮件或更改系统或数据库参数。
  • UDF类似于编程语言中使用的函数。函数可以有多个输入变来那个,可以有一个返回值。UDF中,传送到函数的所有变量都是按值传递。UDF还可以返回一种特殊类型的数据类型——表。

用户和角色

用户和角色相互并存。用户(user)等价于登录。简言之,该对象表示登录SQL Server的标识符。登录SQL Server的任何人都映射到一个用户。用户属于一个或多个角色(role)。SQL Server中可以直接赋予用户或角色执行某操作的权限,一个或多个用户可属于同一角色。

规则

规则和约束(CHECK)都是限制插入到表中的数据类型的信息。与规则不同的是,约束本身并不是对象,而是描述特定表的多个元数据。规则是为了向下兼容的需要,在新的开发中应该使用CHECK约束,避免使用规则。

默认值

SQL Server中有两种类型的默认值。包括对象本身的默认值,以及表中特定列的元数据的默认值(而非真正对象)。与此非常类似,约束是针对对象,而规则是针对元数据。当插入一条记录时,如果没有提供该列的值,且该列具有其默认值,则自动插入默认值。

用户自定义的数据类型

用户自定义的数据类型是系统定义数据类型的扩展。自SQL Server 2005版本开始,用户自定义数据类型几乎可定义任意数据。

SQL Server 2005中的数据类型

数据类型名

长度(以字节为单位)

数据特点

Bit 

整形

1 

表中的第一个Bit数据类型占1个字节;其余7个位也用作Bit数据类型。允许空格使其占用一个额外的字节。

Bigint 

整形

8 

可处理日常用到的越来越大的数,其取值范围为-263~263-1

Int 

整形

4 

取值范围为-2147483648~ 2147483647

SmallInt 

整形

2 

取值范围-32768~32768

TinyInt

整形

1 

取值范围0~255

Decimal/Numeric 

数字型

可变

固定精度,取值范围为-1038-1~1038-1

Money 

货币

8 

货币单位,取值范围为-263~263,精确到4个小数位。注意货币单位可以是任意货币,不限于美元。

SmallMoney 

货币

4 

货币单位,取值范围为-214748.3648~214748.3647

Float(Real) 

近似小数

可变

由一参数(如Float(20))决定其长度与精度。注意参数值表示位数,不是字节数。

DateTime 

日期/时间

8 

日期与时间,取值范围为175311~99991231日,精确到0.03秒。

SmallDateTime 

日期/时间

4 

日期与时间,取值范围为190011~207966日,精确到分钟。

Cursor 

特殊小数

1 

指向光标的指针,只占用一个字节,记住组成实际光标的结果集也占内存,占用内存的大小取决于结果集。

Timestamp/rowversion 

特殊小数(二进制)

8 

给定数据库的唯一特定值。即使UPDATE语句没有timestamp列(时间标记),但其值在插入或更新记录的时间自动由数据库设定(不允许直接更新timestamp对象)。

UniqueIdentifier 

特殊小数(二进制)

16 

全球唯一标识符(GUID),必须保证在内存空间和时间内唯一。

Char 

字符

可变

定长字符数据。比设定长度短时使用空格填充,为非Unicode数据,最大长度为8000字符。

VarChar 

字符

可变

长度可变的字符数据。按需存储,为非Unicode数据。允许最大长度为8000字符,但使用max关键字(varchar(max))时表示其长度可足够大(231字节)。

Text 

字符

可变

SQL Server 2005保持向后兼容的需要。可使用varchar(max)代替。

NChar 

Unicode字符

可变

定长Unicode字符数据。最大长度为4000字符,比设定长度短时使用空格填充。

NVarChar 

Unicode字符

可变

长度可变的Unicode字符数据。按需存储。允许最大长度为8000字符,但使用max关键字(nvarchar(max))时表示其长度可足够大(231字节)。

NText 

Unicode字符

可变

SQL Server 2005保持向后兼容的需要。可使用nvarchar(max)代替。

Binary 

二进制

可变

定长二进制数,最大长度为8000字节。

VarBinary 

二进制

可变

可变长度二进制数,最大特定长度为8000字节,可使用max关键字(varbinary(max))使其作为大对象字段(可达231字节)。

Image 

二进制

可变

SQL Server 2005保持向后兼容的需要。可使用varbinary(max)代替

Table 

其他

- 

主要用于结果集,通常作为用户自定义函数返回。在表的定义中不作为可用的数据类型。

SQL_Variant 

其他

- 

用于保存SQL Server数据类型的容器。当列或函数需要处理多种数据类型时可使用这种数据类型。

XML 

字符

可变

定义一字符字段用作XML数据。提供不符合XML模式的数据而面向XML函数的使用的功能。

说明:

SQL Server中没有无符号整数类型。

Server对象标识符

SQL Server中的所有对象都需要命名,即使在创建时没有指定名称(如表中的约束),SQL Server也会自动生成一个名称。

SQL Server中的命名规则非常简单,规则允许名字中内嵌空格,甚至允许名字是关键字。主要的命名规则如下:

  • 对象的名字必须以Unicode 2.0规范定义的任意字母开头。大小写是否敏感取决于服务器配置的方式。
  • 正常对象的名字最多128个字符,临时对象的名字最多116个字符。
  • SQL Server关键字相同或包含内嵌空格的名字必须使用双引号("")或方括号([])。哪一个视为关键字取决于设置数据库的兼容水平。

注意:

只有在设置了SET QUOTED IDENTIFIERON,双引号才可以作为列名中的分界符。特定的对象类型还存在其他命名规则。

2 T-SQL语言基础

T-SQLSQL Server的结构化查询语言的"方言"。T-SQL语言遵守公共语言运行库(CLR),简言之,T-SQL.NET语言。SQL Server 2005可以使用任何.NET语言来访问数据库,而T-SQL只保留了操作SQL Server的核心功能。

基本SELECT语句

SELECT语句的基本语法规则如下:

SELECT

[FROM ]

[WHERE ]

[GROUP BY ]

[HAVING ]

[ORDER BY [ASC|DESC]]

WHERE子句中的逻辑运算符

运算符

实例

功能

=, >, <, >=, <=, <>, !=, !>, !< 

=

= 'Bob' 

标准的比较运算符。要注意
1. "
大于""小于""等于"可能因情况不同而改变。如比较字符串时是否区分大小写。

2. !=<>都表示"不等于",而!<!>分别表示"不小于""不大于"

AND, OR, NOT 

= AND >=

!= "MyLiteral" OR = "MyOtherLiteral" 

标准的逻辑运算符。运算优先级为NOTANDOR

BETWEEN 

BETWEEN 1 AND 5 

第一个值在第二个与第三个值之间时其值为TRUE,其等价于A>=B AND A <= C

LIKE 

Like "ROM%" 

可使用%_作为通配符。%表示可以和任意长度的字符串匹配。_表示和任意的单个字符匹配。[]指定一个字符、字符串或范围,匹配其中的任一个对象。[^]匹配指定字符串以外的任意字符。

IN 

IN (List of Numbers)

IN左边的表达式与IN右边的列表中的任意值匹配时返回TRUE

ALL, ANY, SOME 

比较运算符 (子查询)

子查询中的条件全部/任一满足比较运算符时返回TRUEALL指表达式要匹配结果集中的所有值。ANYSOME相同,在表达式中匹配结果集中的任一值时返回TRUE

EXISTS 

EXISTS (子查询)

子查询返回至少一行记录时为TRUE

常见的统计函数

函数

说明

SUM() 

求和

COUNT() 

统计返回的行数(除非使用COUNT(*),否则会忽略NULL值)

AVG() 

计算平均值

MIN() 

计算最小值

MAX() 

计算最大值

DISTINCT和ALL谓词

DISTINCTALL均放在SELECT的后面。DISTINCT表示去除重复的行,ALL表示保留重复的行。

SELECT语句默认是保留重复行的,使用SELECT DISTINCT 将返回没有重复的结果集(每行多个字段整体没有重复,而不是单个字段没有重复)。DISTINCT还可应用与统计函数中,表示统计时首先去除重复的行,所以"COUNT(DISTINCT OrderID)"将比"COUNT(OrderID)"返回的行更少。但是在AVG函数中使用DISTINCT没有任何意义。

ALL用于保留重复的行,这是SELECT语句的默认设置。但在使用UNION语句时默认会去除重复行,这是可以使用ALL指定保留("SELECT... UNION ALL SELECT...")。

使用INSERT语句添加数据

INSERT语句的语法如下:

INSERT [INTO]

[(column_list)]

VALUES (data_values)

注意:

  • 在插入中可以使用DEFAULT关键字指定列使用默认值,使用NULL关键字指定列为NULL值。
  • 如果要插入和数据与表的每列一一对应,插入语句可以忽略列名列表可选项。
  • 插入数值类型数据不需要使用引号,而插入字符串或者日期型数据时需要使用引号。
  • 常用的日期型数据格式为MM/DD/YYYY以及YYYY-MM-DD

存储过程sp_help

存储过程sp_help的功能是给出任意数据库对象、用户定义的数据类型或SQL Server数据类型的信息。执行存储过程sp_help的语法结构如下:

EXEC sp_help

要查看sales表的属性,只要输入一下命令:

EXEC sp_help sales

INSERT INTO... SELECT语句

INSERT INTO... SELECT语句可完成一次插入一个数据块的功能。其语法结构为INSERT语句与SELECT语句语法结构的组合,如下:

INSERT INTO

[]

FROM

[ON ]

别名

使用AS关键字(可以省略)给列或者表取别名。同一个查询中的多个表中,可以选择哪些表使用别名,哪些表不使用别名,代码中别名和表名可以混合使用,但是只要确定了使用表的别名,则这个表必须一直使用别名。

内部连接(INNER JOIN)

内部连接根据一个或几个相同的字段将记录匹配在一起,仅仅返回那些匹配的记录。示例:

SELECT *

FROM Products p

INNER JOIN Suppliers s

    ON p.SupplierID = s.SupplierID

外部连接(OUTER JOIN)

外部连接语法结构:

SELECT

]

    [,...n]

)

 

1. 表和列名称

表和列的推荐命名规则:

  • 名称的每个单词,首字母大写,其他字母小写。
  • 名称尽量短,但是要具有描述性。
  • 限制使用缩写,只使用大家都能理解的缩写。例如"ID"表示标识、"No"表示数字、"Org"表示组织。
  • 当基于其他表来构建表时,需要在新的表名中包含其他父表的名称。
  • 当名称中有两个单词时,不要用任何分隔符。

 

2. 数据类型

注意没有默认的数据类型

 

3. DEFAULT

如果要使用默认值,就必须紧跟在数据类型之后给定这个值。

 

4. IDENTITY

当你设定一个列为标识列时,SQL Server自动分配一个顺序号给你插入的每个行。注意IDENTITY列和PRIMARY KEY列是完全不同的概念,既不会因为有一个IDENTITY列就说明这个值是唯一的(例如,可以重新设置种子,使用前面用过的值)。IDENTITY值通常用于PRIMARY KEY列,但并不是必须这样使用。

 

5. NOT FOR REPLICATION

NOT FOR REPLICATION参数决定:当列(通过复制)发布到另外一个数据库时,是否为新的数据库分配一个新的标识值,还是保留已有的值。

 

6. NULL/NOT NULL

默认的设置是列值是NOT NULL,除非指定允许为空。然而,有很多不同的设置可以改变这个设置,从而影响这个默认值。

 

7. 列约束

列约束就是对单个列设置的关于该列可插入数据的限制和规则。

 

8. 计算列

该列值是由表中其他列动态生成的。具体的语法如下:

AS

例如:

ExtendedPrice AS Price * Quantity

ListPrice AS Cost * 1.2

相关的限制条件:

  • 不能使用子查询,而且值不能来自其他不同的表。
  • SQL Server 2000之前,不能使用计算列作为键的任何部分,也不能和默认约束一起使用。在SQL Server 2005中,可以在约束中使用计算列。
  • 以前版本的另外一个问题是在计算列中创建索引的能力。可以在计算列上创建索引,但是必须采用特定的步骤。

 

9. 表约束

表约束和列约束很相似,但表约束可以基于多个列。表层次的约束包括PRIMARY KEY约束、FOREIGN KEY约束以及CHECK约束。

 

10. ON

表定义中的ON子句可以指定表位于哪个文件组。大多数时间,可以省略ON子句,那些表将位于默认文件组中。

 

11. TEXTIMAGE_ON

该选择将表的特定部分移动到不同的文件组中。这个子句只有在表的定义中有textntextimage列时才有效。当使用TEXTIMAGE_ON子句时,只是将BLOB信息移动到分离的文件组中——表的其他部分还在默认文件组或者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

)

使用sp_help存储过程查看表的信息:

EXEC sp_help Customers

4.3 ALTER语句

ALTER语句用来更改对象。ALTER语句总是有相同的开头:

ALTER

4.3.1 ALTER DATEBASE

示例:

ALTER DATABASE Accounting

    MODIFY FILE

    (

        NAME = Accounting,

        SIZE = 100MB

    )

4.3.2 ALTER TABLE

更经常的情况是改变表的结构。这个可以是增加、删除一列或者改变一列的数据类型等。示例:

ALTER TABLE Employees

    ADD

        PreviousEmployer VARCHAR(30) NULL,

        DataOfBirth DATETIME NULL,

        LastRaiseDate DATETIME NOT NULL, DEFAULT '2005-01-01'

4.4 DROP语句

DROP语句用来删除对象。

DROP [, ...n]

如果需要,可以同时删除两个表:

USE Accounting

 

DROP TABLE Customers, Employees

删除整个数据库:

DROP DATABASE Accounting

5约束

确保数据的完整性不是使用数据的程序的责任,而是数据库本身的责任。将数据完整性的责任移到数据库本身是数据库管理的一次革命。

3种不同类型的约束:

  • 实体约束
  • 域约束
  • 参照完整性约束

具体的约束类型:

  • PRIMARY KEY约束
  • FOREIGN KEY约束
  • UNIQUE约束
  • CHECK约束
  • DEFAULT约束

5.1 约束的类型

5.1.1 域约束

域约束处理一个或多个列,确保一个特定列或一组特定列满足特定的标准。

5.1.2 实体约束

实体约束都是关于每个行的。这种形式的约束并不关心一个整体的列,只对特定的行感兴趣,如PRIMARY KEY约束和UNIQUE约束。

5.1.3 参照完整性约束

参照完整性约束是在某列的值必须与其他列的值匹配时创建的,列可以在同一个表中,或者更通常的是在不同的表中,如FOREIGN KEY约束。

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 外键约束

CREATE语句中设置一列或几列外键约束的语法如下所示:

FOREIGN KEY REPERENCES

()

    [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. 级联动作

外键是双向的,即不仅是限制子表的值必须存在于父表中,还在每次对父表操作后检查子行。SQL Server的默认行为是在子表存在时"限制"父表不被删除。然而,有时会自动删除任何相关记录,而不是防止删除被引用的记录。同样,在更新记录时,可能希望相关的记录自动引用刚刚更新的记录。这种进行自动删除和更新的过程称为级联。通过修改声明外键的语法——添加ON子句,来定义级联操作。

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

)

如果对外键定义了CASCADE,则操作会从父表级联到子表中。即,如果从父表删除了某项,子表中依赖该项的项都会被删除;如果从父表中更新了某项,则子表中依赖该项的字段也会被更新。

值得注意的是:CASCADE动作所能影响的深度没有限制。

 

4. 其他操作

NO ACTION为默认操作,即如果子表有依赖,则禁止对父表中的该字段进行删除和更新操作。

SET NULL操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为NULL,前提是子表中的该项可为NULL值。

SET DEFAULT操作会在父表中的该字段被删除或者更新时,将子表中的依赖项设为在子表中定义的默认值,当然前提是在子表中该字段有默认值。

5.3.3 唯一约束

唯一约束不会自动防止您设置一个NULL值。是否允许NULL值取决于表中相应列的NULL选项的设置。然而,要记住如果您确实允许NULL值,那么只能插入一个NULL

 

在创建表时设置唯一约束:

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)

在约束名称中的AK前缀代表"交替键(Alternate Key)",也可以使用前缀UQ或者简单的U,代表唯一约束。

5.4 CHECK约束

CHECK约束使用与WHERE字句一样的规则来定义。CHECK约束标准的示例如下:

目标

SQL 

限制Month列为合适的数字

BETWEEN 1 AND 12 

合适的SSN格式

LIKE '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]' 

限制Shippers的一个特定列表

IN ('UPS', 'Fed Ex', 'USPS') 

价格必须为正

UnitPrice >= 0 

在同一行中引用另外一个列

ShipDate >= OrderDate 

 

在已存在的表中添加CHECK约束:

ALTER TABLE Customers

    ADD CONSTRAINT CK_CustomerDateInSystem

        CHECK (DateInSystem <= GETDATE())

试着插入违反CHECK约束的记录会得到错误。

5.5 DEFAULT约束

DEFAULT约束定义了当插入新行时,在您定义了默认约束的列中没有数据时填充的默认值。要注意:

  • 默认值只在INSERT语句中使用——UPDATE语句和DELETE语句中被忽略。
  • 如果在INSERT语句中提供了任意的值(包括NULL值),那么就不使用默认值。
  • 如果没有提供值,那么总是使用默认值。

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 在创建约束时忽略无效的数据

默认情况下,除非已存在的数据满足约束标准,否则SQL Server将不会创建约束。要想在创建约束时不检查已经在表中的数据是否满足约束,可以在添加约束时添加WITH NOCHECK选项。示例:

ALTER TABLE Customers

    WITH NOCHECK

    ADD CONSTRAINT CK_CustomerPhoneNo

    CHECK

    (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 临时使已存在的约束失效

使用NOCHECK选项关闭约束,而不是删除它。示例:

ALTER TABLE Customers

    NOCHECK

    CONSTRAINT CK_CustomerPhoneNo

当准备重新让约束起效时,使用CHECK选项代替NOCHECK

ALTER TABLE Customers

    CHECK

    CONSTRAINT CK_CustomerPhoneNo

6在查询中添加更多内容

6.1 子查询的概念

子查询是嵌套在另外一个查询中的正常的T-SQL查询。在有一个SELECT语句作为部分数据或者另外一个查询的条件的基础时,通过使用括号创建子查询。

子查询通常用于满足下列需求之一:

  • 将一个查询分隔为一系列的逻辑步骤。
  • 提供一个列表作为WHERE子句或者INEXISTSANLSOMEALL的目标
  • 为父查询中的每个记录提供一个查询表。

6.2 嵌套的子查询

嵌套的子查询只在一个方向嵌套——返回在外部查询中使用的单个值,或者在IN运算符中使用的一个完整的值列表。

在最松散的意义上说,查询语法看起来像下面的两个语法模板:

SELECT

FROM

WHERE IN (

    SELECT

    FROM

    WHERE )

6.2.1 使用单个值的SELECT语句的嵌套查询

例如,假设希望知道每一天通过系统销售的产品的每个条目的ProductID

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. 使用返回多个值的子查询的嵌套查询

例如,查看所有具有折扣记录的商店列表:

USE Pubs

 

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和前面示例几乎相同,区别是添加了NOT运算符。这个不同点时的在转化连接语法时设置等于外部连接而不是内部连接。例如,需要查询所有在Pubs数据库中没有匹配的折扣记录的商店:

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 相互关联的子查询的工作原理

在相互关联的子查询中,内部查询在外部查询提供的信息上运行,反之亦然。有3个步骤的处理过程:

  • 外部查询获得一个记录,然后将该记录传递到内部查询。
  • 内部查询根据传递的值执行。
  • 内部查询然后将结果值传回到外部查询,外部查询利用这些值完成处理过程。

6.3.2 在WHERE子句中的相互关联的子查询

例如,需要查询系统中每个顾客第一个订单的OrderIDOrderDate

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 cu.CompanyName,

    (SELECT MIN(OrderDate)

    FROM Orders o

    WHERE o.CustomerID = cu.CustomerID) AS "Order Date"

FROM Customers cu

6.3.4 处理NULL数据——ISNULL函数

ISNULL()接受一个变量或者表达式来验证是否是一个空值。如果值确实是NULL,那么函数返回其他预指定的值。如果原来的值不是NULL,那么返回原来的值。语法如下:

ISNULL(, )

因此,示例如表所示:

ISNULL表达式

返回值

ISNULL(NULL, 5) 

5 

ISNULL(5, 15) 

5 

ISNULL(MyColumnName, 0) where MyColumnName IS NULL 

0 

ISNULL(MyColumnName, 0) where MyColumnName = 3 

3 

ISNULL(MyColumnName, 0) where MyColumnName = 'Fred Farmer' 

Fred Farmer 

使用示例:

SELECT cu.CompanyName,

    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 派生表

例如,现在需要查询既订购了Chocolade又订购了Vegie-spread的所有公司名称。查询代码如下所示:

SELECT DISTINCT c.CompanyName

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运算符

使用EXISTS时,根据是否存在数据满足查询中EXISTS语句所建立的标准,返回一个简单的TRUEFALSE。例如:

SELECT CustomerID, CompanyName

FROM Customers cu

WHERE EXISTS (

    SELECT OrderID

    FROM Orders o

    WHERE o.CustomerID = cu.CustomerID)

当使用EXISTS关键字时,SQL Server不需要执行一行一行的连接,而是寻找记录,知道找到第一个匹配的记录,停止在那里。只要有一个匹配,EXISTS就为真,不需要继续查找。

如果需要查询没有订购任何产品的客户,可以使用NOT EXISTS

SELECT CustomerID, CompanyName

FROM Customers cu

WHERE NOT EXISTS (

    SELECT OrderID

    FROM Orders o

    WHERE o.CustomerID = cu.CustomerID)

6.5 数据类型转换:CAST和CONVERT

CASTCONVERT都可以执行数据类型转换。在大部分情况下,两者执行相同的功能,不同的是CONVERT还提供一些日期格式转换,而CAST没有这个功能。

注意,CASTANSI兼容的,而CONVERT不是。

各自的语法如下:

CAST (expression AS data type)

CONVERT (data type, expression[, style])

CASTCONVERT可以进行很多数据类型转换,在SQL Server不进行隐式转换时,需要这种转换。例如:

SELECT 'The Customer has an Order numbered ' + CAST(OrderID AS VARCHAR)

FROM Orders

WHERE CustomerID = 'ALFKI'

例如,需要将timestamp列转换为正常数字。一个timestamp是个二进制数字,因此需要转换:

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

CONVERT还可以控制日期格式:

SELECT OrderDate, CONVERT(VARCHAR, OrderDate, 111) AS "Converted"

FROM Orders

WHERE OrderID = 11050

CONVERT函数最后一个代码说明需要的格式。注意,任何以超过100表示的是4位的年份;小于100的是两位数字的年份,不过有很少的一些例外,并且小于100表示的格式加上100后即为对应的4位的年份表示的格式。

7视图

视图的核心实际上仅仅是一个存储的查询。重要的是可以将来自于基本表(或者其他视图)的数据混合以及匹配,以创建在大多数方面上像另一个基本表那样起作用的对象。

7.1 简单的视图

视图语法的基本形式:

CREATE VIEW

[WITH ENCRYPTION]

AS

WHEN THEN

[...n]

[ELSE ]

END

第二种方法将提供一个表达式,其中每个WHEN子句的值将为TRUE或者FALSE。相关文档把它称为搜索CASE:

CASE

WHEN THEN

[...n]

[ELSE ]

END

可以使用CASE语句最好的方式是把它与SELECT语句放一起使用。

 

1. 简单CASE

简单CASE使用结果等于布尔值的表达式。示例:

USE Northwind

GO

 

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'

END

FROM Orders

 

2. 搜索CASE

搜索CASE语句和简单CASE语句非常相同,它只有两个很细微的不同点:

  • 没有输入表达式。
  • WHEN表达式必须为布尔值。

示例:

USE Northwind

GO

 

SELECT TOP 10 OrderID % 10 AS "Last Digit", ProductID, "How Close?" =

    CASE

        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'

    END

FROM OrderDetails

WHERE ProductID < 10

ORDER BY OrderID DESC

 

注意SQL Server求值的工作方式:

  • 即使两个条件都为真,但只使用第一个条件。
  • 不需要使用"break"语句,在一个条件满足后自动终止。
  • 可以在条件表达式中混合和匹配正在使用的字段。
  • 只要最后等于布尔值的结果,则可以执行任何表达式。

9.5.3 使用WHILE语句循环

语法如下:

WHILE

     |

[BEGIN

    

    [BREAK]

    |

    [CONTINUE[

END]

在WHILE语句中必须跟上BEGIN...END,其中包含整个语句块。

9.6 通过返回值确认成功或失败

返回值指示了存储过程的成功或者失败,甚至是成功或失败的范围或属性。

 

RETURN的工作方式

不管是否提供返回值,程序都会收到一个返回值。SQL Server默认地会在完成存储过程时自动返回0。

为了从存储过程向调用代码返回值,只需要使用RETURN语句:

RETURN []

注意:

  • 返回值必须是整数。
  • RETURN语句是无条件地从存储过程中退出的。

示例:

USE Northwind

GO

 

CREATE PROC spTestReturns

AS

    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

第10章 用户自定义函数

用户自定义函数和存储过程非常相似,但它们也有一些行为和能力的区别。

10.1用户自定义函数的定义

用户自定义函数(UDF)是有序的T-SQL语句集合,该语句集合能够预先优化和编译,并且可以作为一个单元来调用。它和存储过程的主要区别在于返回结果的方式。为了能支持多种不同的返回值,UDF比存储过程有更多地限制。

可以在使用存储过程的时候传入参数,也可以以参数的形式得到返回值。存储过程可以返回值,不过该值是为了指示成功或失败的,而非返回数据。

然而,可以在使用UDF的时候传入参数,但是可以不传出任何值。UDF还可以返回标量(scalar)值,这个值可以是大部分SQL Server的数据类型。UDF还可以返回表。

按照返回值的类型,UDF有两种类型:

  • 返回标量的UDF
  • 返回表的UDF

10.2 返回标量值的UDF

这种类型的UDF和大多数SQL Server内建的函数一样,会向调用脚本或存储过程返回标量值,例如GETDATE()USER()函数就会返回标量值。

UDF可以返回除了BLOBCURSORTIMESTAMP以外的任何SQL Server中有效的数据类型(包含用户自定义类型)。如果想返回整数,UDF也和存储过程不同的是:

  • UDF返回值的目的是提供有意义的数据,而不是说明成功或失败。
  • 在查询中可以内联地执行函数,而使用存储过程则不行。

示例——返回去掉时分秒的日期:

CREATE FUNCTION DayOnly(@Date DATETIME)

RETURNS VARCHAR(12)

AS

BEGIN

    RETURN CONVERT(VARCHAR(12), @Date, 101)

END

函数的使用方法如下:

SELECT *

FROM Orders

WHERE DayOnly(OrderDate) = DayOnly(GETDATE())

在一个UDF中调用另一个UDF

CREATE FUNCTION AveragePrice()

RETURNS MONEY

WITH SCHEMABINDING

AS

BEGIN

    RETURN (SELECT AVG(Price) FROM Titles)

END

GO

 

CREATE FUNCTION PriceDifference(@Price MONEY)

RETURN MONEY

AS

BEGIN

    RETURN @Price – AveragePrice()

END

使用UDF可以大大增加查询语句的可读性,并实现了代码重用:

USE pubs

SELECT Title,

    Price,

    AveragePrice() AS Average,

    PriceDifference(Price) AS Difference

FROM Titles

WHERE Type = 'popular_comp'

10.3 返回表的UDF

可以对UDF返回的表执行JOIN,甚至对结果应用WHERE条件。相对简单的函数示例如下:

USE pubs

GO

 

CREATE FUNCTION fnAuthorList()

RETURN TABLE

AS

RETURN (

SELECT au_id,

    au_lname + ', ' + au_fname AS au_name

    address AS address1,

    city + ', ' + state + ', ' + zip AS address2

FROM authors

)

GO

这样的话,使用这个函数就像使用表一样:

SELECT *

FROM fnAuthorList()

使用返回表的UDF比使用视图的好处在于可以在UDF中将条件参数化,而视图不得不包含不想要的数据,然后再通过WHERE子句过滤。例如:

USE pubs

GO

 

CREATE FUNCTION fnSalesCount(@SalesQty BIGINT)

RETURNS TABLE

AS

RETURN (

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

)

为了执行该函数,只需要调用它并提供参数:

SELECT *

FROM fnSalesCount(25)

再进一步,如果需要查询每一个销售超过25本书以上的作者和出版社的信息,这需要连接UDF返回的表:

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 事务

事务是关于原子性(atomicity)的。原子性的概念是指可以把一些东西当作一个单元来看待。

事务要有非常明确的开始和结束点。事实上,在SQL Server中发出的每一个SELECTINSERTUPDATEDELETE语句都是隐式事务的一部分。即使只发出一条语句,也会把这条语句当作一个事务——要么执行语句中的所有内容,要么什么都不执行。确实,这一条语句默认地将作为事务的长度。

关于事务的操作有:

  • BEGIN事务:设置起始点。
  • COMMIT事务:使得事务成为数据库中永久的、不可撤回的一部分。
  • ROLLBACK事务:本质上说想要忘记它曾经发生过。
  • SAVE事务:创建一个特有的标记符,从而可以做部分的回滚工作。

11.1.1 BEGIN TRAN

语法如下:

BEGIN TRAN|TRANSACTION [|<@transaction variable>]

11.1.2 COMMIT TRAN

事务的提交是完成事务的终点。COMMIT的语法类似于BEGIN

COMMIT TRAN|TRANSACTION [|<@transaction variable>]

11.1.3 ROLLBACK TRAN

ROLLBACK可以回到开始的地方或者其中的任何一个保存点。ROLLBACK的语法如下:

ROLLBACK TRAN|TRANSACTION [ | | <@transaction variable> | <@savepoint variable>]

11.1.4 SAVE TRAN

保存事务从本质上说是创建书签。在建立"书签"之后,可以在回滚中引用它。它的好处是可以回滚到代码中想要的点上。SAVE的语法如下:

SAVE TRAN|TRANSACTION [|<@savepoint variable>]

关于保存点需要记住的是ROLLBACK会清除它们——执行ROLLBACK后之前保存过的保存点都会消失。

11.2 SQL Server记录日志的工作方式

在数据库的正常操作中,大多数执行的活动都是"记录"在事务日志上,而非直接写入数据库中。检查点是指强制地把数据库现在所使用的脏页写入磁盘的周期性操作。脏页是指日志或数据页,它们在读入到缓存后已经被修改,但是所进行的修改还没有写入到磁盘。

11.2.1 失败和恢复

恢复发生在SQL Server每次启动的时候。SQL Server获得数据库文件,并且在最后的检查点以后应用日志中的任何提交的改变。日志中任何没有对应提交的改变都会回滚。

第12章 触发器

一些常见的使用触发器的情况包括:

  • 实施参照完整性,例如数据库或服务器中的参照完整性以及许多复杂的关系类型。
  • 创建审计跟踪。
  • CHECK约束的功能相似,但是用于表、数据库、甚至是服务器之间。
  • 用自己的语句代替用户的操作语句(通常用于允许复杂语句中的插入操作)。

12.1 触发器的概念

触发器是一种特殊类型的存储过程,响应特定的事件。有两种类型的触发器:数据定义语言(DDL)触发器和数据操作语言(DML)触发器。

DDL触发器激活了人们以某些方式(CREATEALTERDROP等)对数据库结构进行修改的响应。DML触发器是一些加在特殊表或试图上的代码片段。只要加在触发器上的事件在表中发生,触发器中的代码就会自动地运行。不能显式地调用触发器——唯一的做法是执行指派给表所需的操作。触发器也没有参数和返回值,因为都不需要。

SQL Server中可以使用3种类型的触发器,并可以相互混合和匹配:

  • INSERT触发器
  • DELETE触发器
  • UPDATE触发器

注意,有些语句不会激活触发器,比如TRUNCATE TABLE有与DELETE语句相似的删除行的效果,但是不会触发任何DELETE触发器。

除了触发器需要加在一个表上外,创建触发器的语法类似于其他CREATE语法:

CREATE TRIGGER

    ON [.]

    [WITH ENCRYPTION]

    {{{FOR | ALTER} [DELETE] [,] [INSERT} [,] [UPDATE]}}

AS

    

12.1.1 ON子句

对创建触发器的对象进行命名。注意如果触发器的类型是AFTER(或FOR)触发器,那么ON字句的目标就必须是一个表(而不能是视图),视图只接受INSTEAD OF触发器。

12.1.2 WITH ENCRYPTION子句

加密触发器代码。注意ALTER语句不会自动加密,如需加密需要再次指明WITH ENCRYPTION选项。

12.1 3 FOR|AFTER子句

还需要对激活触发器的定时时间做出选择。虽然可以使用长期接触的FOR触发器(也可以使用关键字ATFER来替换),而且这也是人们经常考虑的一种触发器,但是也可以使用INSTEAD OF触发器。对这两种触发器的选择将影响到是在修改数据之前还是之后来输入触发器。

SQL Server会将两张表放在一起——其中的INSERTED表保存插入记录的副本,另一张DELETED表保存删除的任何记录的副本。使用INSTEAD OF触发器,创建这两张工作表是发生在检查任何约束之前,而使用FOR触发器,这些表的创建是发生在检查约束之后。使用INSTEAD OF触发器的重点在于可以在视图中清除任何不确定的插入问题。这也意味着在检查约束之前可以采取运动清除违反约束的情况。

使用FORATFER声明的触发器,与INSTEAD OF触发器最大的区别在于它们是在检查完约束之后建立工作表的。

FORAFTER)子句指明了想要在哪种动作下激活触发器。例如:

FOR INSERT, DELETE

注意之前提到过,使用FORAFTER子句声明的触发器只能加在表上,而不允许加在视图上。

 

1. INSERT触发器

每当有人向表中插入全新的数据行的时候,都会执行在代码中通过FOR INSERT标记声明的触发器的代码。对于插入的每一行来说,SQL Server会创建该新行的副本并把它插入到称为INSERTED的表中,该表只在触发器的作用域内存在。

 

2. DELETE触发器

每个删除的额记录的副本将插入到成为DELETED表中,该表同样只在触发器的作用域内存在。

 

3. UPDATE触发器

SQL Server会把每一行当作先删除了现有的记录,并插入了全新的行,所以INSERTEDDELETED表均存在。当然,这两个表会有完全相同数量的数据行。而DELETED表中的为改变前的数据,INSERTED表中为改变后的数据。

12.2 为了数据完整性规则使用触发器

触发器可以完成CHECK约束和DEFAULT约束一样的功能,但可以使用CHECK约束和DEFAULT约束完成的功能不应该再设置触发器。但触发器还是可以完成更多的功能:

  • 业务规则需要引用单个表中的数据。
  • 业务规则需要检查更新的增量(更新前后的区别)。
  • 需要一个定制的错误信息。

12.2.1 处理来自于其他表的需求

例如,客户支持部门的人员不断发出已经停止供应的产品的订单,应该在订单进入系统之前拒绝这些订单的录入。

CREATE TRIGGER OrderDetailNotDiscontinued

    ON OrderDetails

    FOR INSERT, UPDATE

AS

    IF EXISTS (

        SELECT 'TRUE'

        FROM Inserted i

        INNER JOIN Products p

            ON i.ProductID = p.ProductID

        WHERE p.Discontinued = 1)

    BEGIN

        PAISERROR('Order Item is discontinued. Transaction Failed.', 16, 1)

        ROLLBACK TRAN

    END

12.2.2 使用触发器来检查更新的增量

例如,Northwind的存货部门要求不能发出任何销售某个产品超过其一般库存单位的订单。

CREATE TRIGGER ProductIsRationed

    ON Products

    FOR UPDATE

AS

    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

    )

    BEGIN

        RAISERROR('Cannot reduce stock by more than 50%% at onece.', 16, 1)

        ROLLBACK TRAN

    END

12.3 可以关闭触发器而不删除它

可以使用ALTER TABLE语句来打开或关闭触发器,语法如下:

ALTER TABLE

{ENABLE|DISABLE} TRIGGER {ALL|}

12.4 删除触发器

和删除其他对象一样:

DROP TRIGGER

转载于:https://www.cnblogs.com/qwertWZ/archive/2013/05/05/3061743.html

你可能感兴趣的:(《SQL Server 2005 编程入门经典》第一到十二章)