创建和维护数据库概述

客户端/服务器型数据库系统由程序和数据结构两部分组成：

  • 程序为基于客户端的用户数据访问提供界面。

  • 数据库结构管理和存储服务器上的数据。

例如，如果使用 Microsoft® SQL Server™ 2000 创建支票帐户应用程序，则必须建立一个数据库结构来管理帐户交易数据，还要建立一个应用程序充当数据库的用户界面，从而使用户得以访问支票帐户的信息。

要创建能够满足业务需要的数据库，要求对如何设计、创建和维护各个组件有深刻的理解，这样才能确保数据库最佳地运行。

主题 | 描述
数据库 | 描述怎样使用数据库表示、管理和访问数据。
表 | 描述怎样使用表存储数据行和定义多个表之间的关系。
索引 | 描述怎样使用索引提高访问表中数据的速度。
视图 | 描述各种视图及其作用，它们的作用在于为查看一个或多个表中的数据提供变通方式。
存储过程 | 描述这些 Transact-SQL 程序是怎样将业务规则、任务和进程集中在服务器内的。
用触发器强制执行业务规则 | 描述作为特殊类型存储过程的触发器的功能，只有修改表中的数据时才执行这类存储过程。
全文索引 | 描述全文索引是如何提高对存储在基于字符的列中数据（如 varchar 和 text）的查询功能的。


数据库

在 Microsoft® SQL Server™ 2000 中，数据库由包含数据的表集合和其它对象（如视图、索引、存储过程和触发器）组成，目的是为执行与数据有关的活动提供支持。存储在数据库中的数据通常与特定的主题或过程（如生产仓库的库存信息）相关。

SQL Server 能够支持许多数据库。每个数据库可以存储来自其它数据库的相关或不相关数据。例如，服务器可以有一个数据库存储职员数据，另一个数据库存储与产品相关的数据。另一种方案是，一个数据库可以存储当前客户的订单数据，而另一个相关的数据库可以存储用于年度报告的历史客户订单。

在创建数据库之前，理解数据库的各组成部分及其设计方法，以确保实现数据库后，数据库能很好地运行是很重要的。

重要 强烈建议不要在 master 数据库中创建任何用户对象（如表、视图、存储过程或触发器）。master 数据库包含系统表，这些系统表存储 SQL Server 所用的系统信息（如配置选项设置）。


数据库各组成部分

在 Microsoft® SQL Server™ 2000 中，数据库由存储特定结构化数据集的表集合组成。表中包含行（有时称作记录或元组）和列（有时称作特性）的集合。表中的每一列都设计为存储某种类型的信息（例如，日期、名称、美元金额或数字）。表上有几种控制（约束、规则、触发器、默认值和自定义用户数据类型）用于确保数据的有效性。表上可以有索引（与书中的索引非常相似），利用索引可以快速地找到行。可将声明引用完整性 (DRI) 约束添加到表上，以确保不同表中相互关联的数据保持一致。数据库还可以存储过程，这些过程使用 Transact-SQL 编程代码对数据库中的数据执行操作，如存储对表数据提供自定义访问的视图。

例如，创建名为 MyCoDB 的数据库来管理公司的数据。在 MyCoDb 数据库中，可以创建一个名为 Employees 的表来存储每个职员的信息，该表可以包含名为 EmpId、LastName、FirstName、Dept 和 Title 的列。必须给表添加一些约束，以确保没有两个职员使用同一 EmpId，同时确保 Dept 列仅包含贵公司中有效的部门编号。您可以定义索引，以确保能够根据职员 ID 或姓氏快速地找到职员的数据。对于每一个职员，都需要向 Employees 表添加一行数据，所以可以创建名为 AddEmployee 的过程，将该过程自定义为接受新职员的数据值，并执行向 Employees 表中添加行的操作。可能需要一份部门职员的概要，这种情况下定义一个名为 DeptEmps 的视图，合并 Departments 和 Employees 表中的数据并产生输出。下图显示了创建的 MyCoDB 数据库的局部。


文件和文件组

Microsoft® SQL Server™ 2000 使用一组操作系统文件映射数据库。数据库中的所有数据和对象（如表、存储过程、触发器和视图）都存储在下列操作系统文件中：

  • 主要

    该文件包含数据库的启动信息，并用于存储数据。每个数据库都有一个主要数据文件。

  • 次要

    这些文件含有不能置于主要数据文件中的所有数据。如果主文件可以包含数据库中的所有数据，那么数据库就不需要次要数据文件。有些数据库可能足够大故需要多个次要数据文件，或使用位于不同磁盘驱动器上的辅助文件将数据扩展到多个磁盘。

  • 事务日志

    这些文件包含用于恢复数据库的日志信息。每个数据库都必须至少有一个日志文件。

例如，创建简单的数据库 sales 时，可以只使用一个包含所有数据和对象的主文件和一个包含事务日志信息的日志文件。另一种情况是，创建更复杂的数据库 orders 时，可以使用一个主文件和五个辅助文件，数据库内的数据和对象扩展到所有的六个文件中，另外有四个日志文件包含事务日志信息。

文件组允许对文件进行分组，以便于管理和数据的分配／放置。例如，可以分别在三个硬盘驱动器上创建三个文件（Data1.ndf、Data2.ndf 和 Data3.ndf），并将这三个文件指派到文件组 fgroup1 中。然后，可以明确地在文件组 fgroup1 上创建一个表。对表中数据的查询将分散到三个磁盘上，因而性能得以提高。在 RAID（独立磁盘冗余阵列)条带集上创建单个文件也可以获得相同的性能改善。然而，文件和文件组使您得以在新磁盘上轻易地添加新文件。另外，如果数据库超过单个 Microsoft Windows NT® 文件的最大大小，则可以使用次要数据文件允许数据库继续增长。

文件和文件组的设计规则

文件和文件组的设计规则包括：

  • 文件或文件组不能由一个以上的数据库使用。例如，文件 sales.mdf 和 sales.ndf 包含 sales 数据库中的数据和对象，任何其它数据库都不能使用这两个文件。

  • 文件只能是一个文件组的成员。

  • 数据和事务日志信息不能属于同一文件或文件组。

  • 事务日志文件不能属于任何文件组。

请参见

CREATE DATABASE

物理数据库文件和文件组

在文件组上放置表

事务日志


默认文件组

数据库由一个主文件组和任意用户定义的文件组组成。

包含主文件的文件组为主文件组。在创建数据库时，主文件组包含主要数据文件和任何其它未放入另一个文件组中的文件。所有系统表都分配在主文件组中。如果主文件组空间耗尽，那么将不能向系统表添加新目录信息。只要在下列情况下，主文件组才会填满：关闭了自动增长选项，或者含有主文件组中文件的所有磁盘空间耗尽。如果发生这种情况，要么重新打开自动增长，要么将其它文件从磁盘上移走以释放更多的空间。

用户定义的文件组是指在首次创建或以后更改数据库时，用户明确创建的任何文件组。如果用户定义的文件组填满，那么只有明确地分配到该文件组的用户表会受到影响。

在任何时候，有且仅有一个文件组被指定为 DEFAULT 文件组。创建数据库中的对象时，如果没有指定其所属的文件组，那么就将这些对象指派到默认文件组。默认文件组必须足够大，以容纳未分配到用户定义文件组中的所有对象。在初始情况下，主文件组是默认文件组。

可用 ALTER DATABASE 语句更改默认文件组。通过更改默认文件组，创建时没有指定文件组的任何对象都被分配到新默认文件组中的数据文件。然而，系统对象和表仍然保留在 PRIMARY 文件组内，而不是新默认文件组。

更改默认文件组可防止未明确地在用户定义文件组上创建的用户对象与系统对象和表争夺数据空间。

请参见

ALTER DATABASE


使用文件和文件组

文件组对组内的所有文件都使用按比例填充策略。当将数据写入文件组时，Microsoft® SQL Server™ 2000 根据文件中的可用空间量将一定比例的数据写入文件组的每个文件，而不是将所有的数据先写满第一个文件，接着再写入下一个文件。例如，如果文件 f1 有 100 兆字节 (MB) 可用空间，文件 f2 有 200 MB 可用空间，则从文件 f1 中分配一个扩展盘区，从文件 f2 中分配两个扩展盘区，依此类推。这样，两个文件几乎同时填满，并且可获得简单的条带化。

一旦文件组内的所有文件已满，SQL Server 就自动地采用循环方式一次扩展一个文件，以容纳更多的数据（假定数据库设置为自动增长）。例如，某个文件组由三个文件组成，它们都设置为自动增长。当文件组中的所有文件的空间用完时，只扩展第一个文件。当第一个文件已满，不能将更多的数据写入该文件组时，扩展第二个文件。当第二个文件已满，不能将更多的数据写入该文件组时，扩展第三个文件。如果第三个文件已满，不能将更多的数据写入该文件组，那么再次扩展第一个文件，依此类推。

使用文件和文件组时，通过允许跨多个磁盘、多个磁盘控制器或 RAID（独立磁盘冗余阵列）系统创建数据库，可提高数据库性能。例如，如果计算机中有四个磁盘，那么可以创建一个由三个数据文件和一个日志文件组成的数据库，每个磁盘上放置一个文件。在对数据进行访问时，四个读/写磁头可以同时并行地访问数据，从而加速数据库操作。

另外，文件和文件组允许数据布局，因为可以在特定的文件组中创建表。因为特定表的所有输入/输出都可以定向到特定的磁盘，所以性能得以改善。例如，可以将最常用的表放在一个文件组中的一个文件中，该文件组位于一个磁盘上；而将数据库中其它的不常访问的表放在另一个文件组中的其它文件中，该文件组位于第二个磁盘上。

建议

这些是对于文件和文件组的一些一般性建议：

  • 大多数数据库在只有单个数据文件和单个事务日志文件的情况下可以很好地运行。

  • 如果使用多个文件，请为附加文件创建第二个文件组，并将其设置为默认文件组。这样，主文件将仅包含系统表和对象。

  • 若要获得最佳性能，请在尽可能多的可用本地物理磁盘上创建文件或文件组，并将争夺空间最激烈的对象置于不同的文件组中。

  • 使用文件组以允许将对象放置在特定的物理磁盘上。

  • 将在同一联接查询中使用的不同表置于不同的文件组中。由于采用并行磁盘输入/输出对联接数据进行搜索，所以性能将得以改善。

  • 将最常访问的表和属于这些表的非聚集索引置于不同的文件组上。如果文件位于不同的物理磁盘上，由于采用并行输入/输出，所以性能得以提高。

  • 不要将事务日志文件与其它文件和文件组置于同一物理磁盘上。

请参见

CREATE DATABASE

物理数据库文件和文件组

在文件组上放置表

事务日志


使用文件和文件组管理数据库增长

在使用文件和文件组创建数据库时，必须指定文件的初始大小。Microsoft® SQL Server™ 2000 根据用户提供的大小创建数据文件。随着数据不断地添加到数据库，这些文件将逐渐变满。然而，如果被添加到数据库中的数据多于文件的容量，就需要考虑数据库增长以超过所分配初始空间的条件及方法。

默认情况下，SQL Server 允许数据文件根据需要尽可能地增长，直到磁盘空间用完为止。因此，如果数据库文件不允许增长到大于创建时的初始值，就必须使用 SQL Server 企业管理器或 CREATE DATABASE 语句在创建数据库时指定。

另外，SQL Server 允许创建在数据填满时自动增长的数据文件，但仅能增长到预定义的最大值。这可以防止完全耗尽磁盘驱动器的空间。

建议

在创建数据库时，请根据数据库中预期的最大数据量，创建尽可能大的数据文件。允许数据文件自动增长，但要有一定的限度，为此需指定数据文件增长的最大值，以便在硬盘上留出一些可用空间。这样，当添加的数据多于预期值时允许数据库增长，但不会填满磁盘驱动器。如果超过了数据文件初始值，而且文件开始自动增长，那么请重新评估预期的数据库最大值，并通过在必要时添加更多的磁盘空间、在数据库中创建并添加更多的文件或文件组的方法，作出相应的计划。

然而，如果假定不使数据库增长到超过其初始值，请将数据库增长的最大值设置为零。这将防止数据库文件增长。如果数据库文件已填满数据，那么只有为数据库添加更多的数据文件或扩展现有的文件后，才能添加更多的数据。

文件碎片

如果大量的文件共享同一磁盘，允许文件自动增长可能会使这些文件产生碎片。因此，建议在尽可能多的不同可用本地物理磁盘上创建文件或文件组。将争夺空间最激烈的对象置于不同的文件组中。

请参见

ALTER DATABASE

CREATE DATABASE


只读文件组

Microsoft® SQL Server™ 2000 允许将文件组标记为只读。任何现有的文件组（主文件组除外）都可以标记为只读。不能使用任何方式对标记为只读的文件组进行修改。

将不允许修改的表（如历史数据）置于一些文件组上，然后将这些文件组标记为只读。这会防止意外的更新。这样一来，可以对只读文件组进行备份，并在其它运行 SQL Server 的服务器上还原，而不用考虑恢复事务日志。

请参见

ALTER DATABASE


事务日志

在 Microsoft® SQL Server™ 2000 中，数据库必须至少包含一个数据文件和一个事务日志文件。数据和事务日志信息从不混合在同一文件中，并且每个文件只能由一个数据库使用。

SQL Server 使用各数据库的事务日志来恢复事务。事务日志是数据库中已发生的所有修改和执行每次修改的事务的一连串记录。事务日志记录每个事务的开始。它记录了在每个事务期间，对数据的更改及撤消所做更改（以后如有必要）所需的足够信息。对于一些大的操作（如 CREATE INDEX），事务日志则记录该操作发生的事实。随着数据库中发生被记录的操作，日志会不断地增长。

事务日志记录页的分配和释放，以及每个事务的提交或回滚。这允许 SQL Server 采用下列方式应用（前滚）或收回（回滚）每个事务：

  • 在应用事务日志时，事务将前滚。SQL Server 将每次修改后的映象复制到数据库中，或者重新运行语句（如 CREATE INDEX）。这些操作将按照其原始发生顺序进行应用。此过程结束后，数据库将处于与事务日志备份时相同的状态。

  • 当收回未完成的事务时，事务将回滚。SQL Server 将所有修改前的映象复制到 BEGIN TRANSACTION 后的数据库。如果遇到表示执行了 CREATE INDEX 的事务日志记录，则会执行与该语句逻辑相反的操作。这些前映象和 CREATE INDEX 逆转将按照与原始顺序相反的顺序进行应用。

在检查点处，SQL Server 确保所有已修改的事务日志记录和数据库页都写入磁盘。在重新启动 SQL Server 时所发生的各数据库的恢复过程中，仅在不知道事务中所有的数据修改是否已经从高速缓冲中实际写入磁盘时才必须前滚事务。因为检查点强迫所有修改的页写入磁盘，所以检查点表示启动恢复必须开始前滚事务的位置。因为检查点之前的所有修改页都保证在磁盘上，所以没有必要前滚检查点之前已完成的任何事务。

利用事务日志备份可以将数据库恢复到特定的即时点（如输入不想要的数据之前的那一点）或故障发生点。在媒体恢复策略中应考虑利用事务日志备份。有关更多信息，请参见选择恢复模型。

请参见

CREATE DATABASE

事务

事务日志备份


虚拟日志文件

每个事务日志文件都被逻辑地分成称为虚拟日志文件的较小的段。虚拟日志文件是事务日志的截断单位。当虚拟日志文件不再包含活动事务的日志记录时，可以对其进行截断处理，使其空间可用于记录新事务。

虚拟日志文件的最小大小为 256 千字节 (KB)。事务日志的最小大小为 512 KB，它可以提供两个 256 KB 大小的虚拟日志文件。事务日志中的虚拟日志文件数目和大小随日志文件大小的增加而增加。小日志文件可以包含较少量的小虚拟日志文件（例如，5 MB 的日志文件包含 5 个 1 MB 的虚拟日志文件）。大日志文件可以有较大的虚拟日志文件（例如，500 MB 的日志文件包含 10 个 50 MB 的虚拟日志文件）。

Microsoft® SQL Server™ 2000 力求避免包含太多的小虚拟日志文件。虚拟日志文件数目的增长比其大小的增长要慢得多。如果日志文件以小增量增长，则它趋向于包含多个小虚拟日志文件。如果日志文件以较大增量增长，SQL Server 则会创建较少数量的较大虚拟日志文件。例如，如果事务日志以 1 MB 增量增长，那么与以 50 MB 增量增长的事务日志相比，其虚拟日志文件的大小将更小，数目将更多。虚拟日志文件数较多时，数据库恢复所需要的执行时间将增加。

随着记录不断地写入日志中，日志的结尾会从一个虚拟日志文件增长到下一个虚拟日志文件。如果数据库有一个以上的物理日志文件，随着日志的增长，日志结尾将穿过每个物理文件中的每个虚拟日志文件，直至循环回到第一个物理文件中的第一个虚拟日志文件。只有当所有的日志文件已满时，日志才开始自动增长。

请参见

收缩数据库


数据库设计考虑事项

设计数据库时，需要理解要为之建模的业务职能和用于表示这些业务职能的数据库概念及功能。

准确地设计数据库以建立业务模型是至关重要的，因为数据库一旦实现完毕，再对其设计进行更改将花费大量的时间。另外，设计良好的数据库执行情况更好。

在设计数据库时，应考虑以下事项：

  • 数据库的用途及该用途将如何影响设计。应创建符合用途的数据库计划。

  • 数据库规范化规则，防止数据库设计中出现错误。

  • 对数据完整性的保护。

  • 数据库和用户权限的安全要求。

  • 应用程序的性能需求。设计数据库时必须利用 Microsoft® SQL Server™ 2000 中能够提高性能的功能。对于性能而言，在数据库大小和硬件配置之间权衡也是很重要的。

  • 维护。

  • 估计数据库大小。


创建数据库计划

创建数据库的第一步是制订计划，该计划可在实现数据库时用作指南；也可以在数据库实现完成后，用作数据库的功能说明。数据库设计的复杂性和细节由数据库应用程序的复杂性和大小及用户数确定。

数据库应用程序的性质和复杂性以及对其进行计划的过程可以变化很大。数据库可以相对简单，如为单人使用设计的数据库；它也可以很大、很复杂，如为处理数百万顾客的所有银行事务而设计的数据库。对于前一种情况，数据库设计可能只需要在一些草稿纸上写几个备注即可。对于后一种情况，设计可能是数百页的正式文档，其中包含有关数据库的各种可能细节。

在计划数据库时，不管其大小和复杂性如何，都要经过下列基本步骤：

  • 收集信息。

  • 标识对象。

  • 建立对象模型。

  • 标识每个对象的信息类型。

  • 标识对象之间的关系。
收集信息

在创建数据库之前，必须对期望数据库执行的工作有一个透彻的理解。如果数据库用于替换基于纸张或手工执行的信息系统，则现有的系统能为您提供所需的大多数信息。与系统中所涉及的每个人进行交流，以找出他们所做的工作以及他们需要从数据库中获得的内容是很重要的。了解他们希望新系统完成的工作，以及找出现有系统的问题、限制和瓶颈也很重要。收集客户结算、库存列表、管理报表以及属于现有系统的任何其它文档的复本，因为这些对于设计数据库和界面非常有用。

标识对象

在收集信息的过程中，必须标识将由数据库管理的主要对象或实体。对象可以是一个有形的实体（如一个人或一个产品），也可以是一个无形的项目（如某项业务事务、公司中的某个部门或某个付薪周期）。通常会有几个主要对象，标识了这些对象之后，相关的项目就会变得清晰。数据库中每个不同的项目都应有一个对应的表。

Microsoft® SQL Server™ 2000 附带的 pubs 示例数据库中的主要对象是书籍。在该公司的业务内，与书籍相关的对象有：撰写该书的作者、生产该书的出版商、销售该书的书店和书店所执行的销售事务。这些对象中每一个都是数据库中的一个表。

建立对象模型

标识了系统中的对象后，使用该系统的一种直观表示方式对这些对象进行记录是很重要的。在数据库实现期间，可以使用数据库模型作为参考。

鉴于此目的，数据库开发人员使用各种工具进行数据库设计，在技术复杂性方面，这些工具可以是简单的铅笔和草稿纸，也可以是字处理或电子表格程序，甚至可以是专门为数据库建模工作设计的软件程序。不管决定要使用哪一种工具，总保持使用最新的工具是很重要的。

SQL Server 企业管理器包括可视化设计工具（如数据库设计器），可用于设计和创建数据库中的对象。有关更多信息，请参见数据库设计器。

标识每个对象的信息类型

将数据库中的主要对象标识为候选表后，下一步是标识必须为各对象存储的信息类型。这些信息是对象的表中的列。数据库表中的列包含几个常见的信息类型：

  • 原始数据列

    这些列存储有形信息（如名称），由数据库外部的源确定。

  • 分类列

    这些列对数据进行分类或分组，并存储有限选择范围的数据（如真/假、已婚/单身、副总裁/主管/组长等等）。

  • 标识符列

    这些列提供对存储在表中的每个项目进行标识的机制。这些列的名称中有一个 id 或 number（例如，employee_id、invoice_number 和 publisher_id）。标识符列是用户和内部数据库处理访问表中的一行数据时所用的主要组件。有时，可以在表中使用对象的有形 ID（如社会保险号），但在大多数情况下，定义表时可以为行创建可靠的人工 ID。

  • 关系或引用列

    这些列建立一个表中的信息和另一个表中相关信息的链接。例如，跟踪销售事务的表通常与 customers 表有链接，以便可以将完整的客户信息与销售事务关联。

标识对象间的关系

关系数据库的一种优势是联系或关联数据库中各种项目的有关信息的能力。可以单独地存储独立的信息类型，但数据库引擎可以在必要时组合数据。在设计过程中，标识对象间的关系需要查看各种表，确定它们逻辑相关的方式，以及添加关系列以建立一个表与另一个表间的链接。

例如，pubs 数据库的设计者已经在数据库中为书名和出版商创建了表。Titles 表包含每种书的信息：一个名为 title_id 的标识符列；书名的原始数据列、书价、出版日期和一些有关书的销售信息列。该表包含名为 type 的分类列，利用该列可按照书中的内容类型对书进行分组。另外，每本书都有一个出版商，但出版商信息是在另一个表中，因此，titles 表有一个 pub_id 列以存储出版商的 ID。当为某一种书添加一行数据时，出版商 ID 与书的其它信息一起存储。


联机事务处理与决策支持

许多应用程序属于数据库应用程序的两种主要类型：

  • 联机事务处理 (OLTP)

  • 决策支持

这两种应用程序类型的特征对数据库的设计考虑事项有很大的影响。

联机事务处理

联机事务处理数据库应用程序最适合于管理变化的数据，通常，这种应用程序有大量的用户同时执行更改实时数据的事务。尽管用户对数据的单个请求一般只引用少量记录，但是，这些请求有许多是同时发生的。这些类型的数据库的常见例子是航空订票系统和银行事务系统。在这种类型的应用程序中，主要关心的是并发性和原子性。

数据库系统中的并发性控制确保两个用户不能更改同一数据，或者一个用户不能在另一个用户对数据操作完成之前对其进行更改。例如，如果您正在与一位航空订票代理联系预订某航班上最后一个可用座位，该代理开始用您的姓名进行座位的预订处理，这时，其他代理应该不能再告诉其他乘客还可以预订该座位。

原子性确保事务中包括的所有步骤都作为一个组成功地完成。如果一个步骤失败，则不应完成其它步骤。例如，某个银行事务可能包括两个步骤：从您的支票帐户中取出资金，然后将其放入您的存款帐户中。如果从您的支票帐户中成功地移走了资金，就需要确保将该资金放入您的存款帐户中或重新放回到您的支票帐户中。

联机事务处理设计注意事项

事务处理系统数据库应设计为支持：

  • 很好的数据放置。

    对于 OLTP 系统，输入/输出瓶颈是一个尤为关心的问题，原因在于修改整个数据库中数据的用户很多。确定数据的可能访问模式，并将经常访问的数据放在一起。在此过程中，可辅以文件组和 RAID（独立磁盘冗余阵列）系统。

  • 缩短事务以将长期锁减至最少，提高并发性。

    在事务期间，避免用户交互。无论何时，只要有可能，就通过执行单个存储过程来处理整个事务。在事务内对表的引用顺序可能会影响并发性。将对经常访问的表的引用置于事务的末尾，以便将控制锁的持续时间减至最短。

  • 联机备份。

    OLTP 系统通常的特征是连续操作（一天 24 小时，一周 7 天），为达到此目的，停工时间要保持绝对最短。尽管 Microsoft® SQL Server™ 2000 可以在数据库正在使用时对其进行备份，但是应将备份过程安排在活动不频繁时进行，以使对用户的影响减至最小。

  • 数据库的高度规范化。

    尽可能减少冗余信息以提高更新的速度，从而提高并发性。减少数据还可以提高备份的速度，因为只需要备份更少的数据。

  • 很少或没有历史或聚合数据。

    可以将很少引用的数据归档到单独的数据库中，或者从经常更新的表中移出，并置于仅含历史数据的表中。这将保持表尽可能地小，从而缩短备份时间，改善查询性能。

  • 小心使用索引。

    每次添加或修改行时，必须更新索引。若要避免对经常更新的表进行过多的索引，索引范围应保持较窄。请用索引优化向导设计索引。

  • OLTP 系统需要最佳的硬件配置以处理较大并发用户数目和快速响应时间。
决策支持

决策支持数据库应用程序最适合于不更改数据的数据查询。例如，公司可以定期地按日期、销售地区或产品汇总其销售数据，并将该信息存储在单独的数据库中以供高级管理人员进行分析。若要作出业务决策，用户必须能够根据各种准则，通过查询数据快速地确定销售趋势。然而，他们不必更改这些数据。决策支持数据库中的表建立了大量索引，原始数据通常要进行预处理，并对其进行组织以支持将要使用的各种类型的查询。因为用户并不更改数据，并发性和原子性问题不必考虑；数据仅定期更改，在非工作时间和低流量时间对数据库进行大容量更新。

决策支持设计注意事项

决策支持系统数据库应设计成可以支持：

  • 大量索引。

    决策支持系统只需要很少的更新，但数据量很大。可使用大量索引提高查询性能。

  • 数据库的非规范化。

    引入预聚合或汇总数据以满足常见的查询要求，缩短查询响应时间。

  • 使用星型架构或雪花架构来组织数据库内的数据。

请参见

创建数据仓库

数据仓库的组成部分


规范化

数据库的逻辑设计（包括各种表和表间关系）是优化关系数据库的核心。设计好逻辑数据库，可以为优化数据库和应用程序性能打下基础。逻辑数据库设计不好，会损害整个系统的性能。

规范化逻辑数据库设计包括使用正规的方法来将数据分为多个相关的表。拥有大量窄表（列较少的表）是规范化数据库的特征。而拥有少量宽表（列较多的表）是非规范化数据库的特征。

通常，合理的规范化将提高性能。当有用的索引可用时，Microsoft® SQL Server™ 2000 查询优化器可高效地在表间选择快速、高效的联接。

规范化的一些益处包括：

  • 排序和索引创建更快。

  • 聚集索引的数目更大。有关更多信息，请参见聚集索引。

  • 索引更窄、更精简。

  • 每个表的索引更少，这样将提高 INSERT、UPDATE 和 DELETE 语句的性能。

  • 空值更少，出现不一致性的机会更少，从而提高数据库紧凑性。

随着规范化的不断提高，检索数据所需的联接数和复杂性也将不断增大。太多表间的关系联接太多、太复杂可能会影响性能。合理的规范化通常很少包括经常性执行的、所用的联接涉及 4 个以上表的查询。

有时，逻辑数据库设计已经确定，全部进行重新设计是不现实的。然而，尽管如此，将大表有选择性地进行规范化处理，分为几个更小的表是可能的。如果是通过存储过程对数据库进行访问，则架构的这种更改可能不会影响应用程序。如果不是这种情况，那么可以创建一个视图，以向应用程序隐藏架构的更改。

获得设计良好的数据库

在关系数据库设计理论中，规范化规则指出了在设计良好的数据库中必须出现或不出现的某些特性。关于规范化规则的完整讨论不属于本主题的范畴。不过，有几个可帮助获得合理的数据库设计的规则：

  • 表应该有一个标识符。

    数据库设计理论的基本原理是：每个表都应有一个唯一的行标识符，可以使用列或列集将任何单个记录同表中的所有其它记录区别开来。每个表都应有一个 ID 列，任何两个记录都不可以共享同一 ID 值。作为表的唯一行标识符的一列或多列是表的主键。

  • 表应只能存储单一类型实体的数据。

    试图在表中存储过多的信息会妨碍对表的数据进行有效、可靠的管理。在 SQL Server 2000 的 pubs 数据库中，书名和出版商的信息存储在两个独立的表内。尽管在 titles 表中同时创建书籍及其出版商的信息列是可能的，但是这种设计会导致几个问题。必须为出版商出版的每本书添加并存储出版商信息，而这是没有必要的，并且会占用数据库中的额外存储空间。如果出版商的地址更改了，则必须对每本书进行更改。出版商的最后一本书从标题表中删除后，该出版商的信息就会丢失。

    在 pubs 数据库中，书和出版商的信息存储在 titles 和 publishers 表中，有关出版商的信息只须输入一次，然后即链接到每本书上。当出版商信息更改时，只需要在数据库中的一个地方进行更改，即使该出版商在数据库中没有书，出版商信息仍然存在。

  • 表应避免可为空的列。

    表中的列可定义为允许空值。空值表示没有值。尽管在个别情况下，允许空值有用，但是最好少用空值，因为空值需要进行特殊的处理，这样会增加数据操作的复杂性。如果某一表中有几个可为空的列，并且列中的几行有空值，则应考虑将这些列置于链接到主表的其它表中。将数据存储在两个独立的表中使主表的设计更为简单，但能够满足存储空值信息的偶尔需要。

  • 表不应有重复的值或列。

    数据库中某一项目的表不应包含特定信息的值列表。例如，pubs 数据库中的书可以是合著的。如果在 titles 表中有一列是作者的名称，这就会出现问题。一种解决方法是：在列中存储两位作者的姓名，但这样会很难显示单个作者的列表。另一种解决方法：是更改表的结构，为第二位作者的姓名添加另一列，但这只能包含两位作者。当然，如果一本书有三位作者，则必须再添一列。

    如果发现需要在单个列中存储值的列表，或者如果单个数据需要多列（au_lname1、au_lname2 等等），则应考虑将重复的数据置于与主表有链接的另一个表中。pubs 数据库有一个表存储书信息，另一个表存储书的 ID 值和书作者的 ID 值。这种设计允许书有任意数目的作者，而不用修改表的定义，同时不用为单个作者的书分配未使用的存储空间。


数据完整性

强制数据完整性可确保数据库中的数据质量。例如，如果输入了 employee_id 值为 123 的职员，那么该数据库不应允许其他职员使用同一 ID 值。如果计划将 employee_rating 列的值范围设定为从 1 到 5，则数据库不应接受 6。如果表有一 dept_id 列，该列存储职员的部门编号，则数据库应只允许接受公司中的有效部门编号。

对表进行计划有两个重要步骤：标识列的有效值和确定如何强制列中的数据完整性。数据完整性有四种类型：

  • 实体完整性

  • 域完整性

  • 引用完整性

  • 用户定义完整性
实体完整性

实体完整性将行定义为特定表的唯一实体。实体完整性强制表的标识符列或主键的完整性（通过索引、UNIQUE 约束、PRIMARY KEY 约束或 IDENTITY 属性）。

域完整性

域完整性是指给定列的输入有效性。强制域有效性的方法有：限制类型（通过数据类型）、格式（通过 CHECK 约束和规则）或可能值的范围（通过 FOREIGN KEY 约束、CHECK 约束、DEFAULT 定义、NOT NULL 定义和规则）。

引用完整性

在输入或删除记录时，引用完整性保持表之间已定义的关系。在 Microsoft® SQL Server™ 2000 中，引用完整性基于外键与主键之间或外键与唯一键之间的关系（通过 FOREIGN KEY 和 CHECK 约束）。引用完整性确保键值在所有表中一致。这样的一致性要求不能引用不存在的值，如果键值更改了，那么在整个数据库中，对该键值的所有引用要进行一致的更改。

强制引用完整性时，SQL Server 禁止用户进行下列操作：

  • 当主表中没有关联的记录时，将记录添加到相关表中。

  • 更改主表中的值并导致相关表中的记录孤立。

  • 从主表中删除记录，但仍存在与该记录匹配的相关记录。

例如，对于 pubs 数据库中的 sales 和 titles 表，引用完整性基于 sales 表中的外键 (title_id) 与 titles 表中的主键 (title_id) 之间的关系。

用户定义完整性

用户定义完整性使您得以定义不属于其它任何完整性分类的特定业务规则。所有的完整性类型都支持用户定义完整性（CREATE TABLE 中的所有列级和表级约束、存储过程和触发器）。

请参见

指定列数据类型

使用约束、默认值和空值


数据安全性

数据库有一种功能是保护数据，防止某些用户查看或更改高度敏感的数据，以及防止所有用户产生代价高昂的错误。Microsoft® SQL Server™ 2000 中的安全系统控制对数据的用户访问和在数据库中执行活动的用户权限。

请参见

设置安全帐户


数据库性能

在设计数据库时，必须确保数据库正确地、快速地执行所有重要的功能。有些性能问题可以在数据库进入生成过程后解决，但另外一些性能问题则可能是数据库设计不良造成的，而且只有通过更改数据库的结构和设计才能加以解决。

在设计和实现数据库时，应找出数据库中的大表和数据库要执行的较复杂处理，在设计这些表时，对性能应特别注意。还要考虑访问数据库的用户数增加对性能的影响。

提高性能的设计更改实例包括：

  • 如果必须对包含成千上万行的表进行汇总以供每日报表使用，可以向该表添加包含预聚合数据的一列或多列，以仅供报表使用。

  • 数据库可能过度规范化，即使用了大量的、小的、相互关联的表来定义数据库。在数据库对这些表中的数据进行处理时，必须执行大量的额外工作以组合相关的数据。这种额外处理可能降低数据库的性能。在这种情况下，适当降低数据库规范化程度以简化复杂处理可以提高性能。

有机地结合数据库的正确设计与索引、RAID（独立磁盘的冗余阵列）和文件组的正确使用，对于获得最佳性能是很重要的。

硬件考虑事项

通常情况下，数据库越大，硬件要求就越高。但还存在其它决定因素：并发用户/会话的数目、事务吞吐量和数据库内的操作类型。例如，学校图书馆的数据库（包含的数据很少进行更新）对硬件的要求比 1兆兆字节 (TB) 大公司的数据仓库（包含经常分析的销售、产品、客户信息）对硬件的要求低。除了磁盘存储要求外，对于数据仓库还需要更多的内存、更快的处理器以使更多的数据能够在内存中高速缓存，引用大量数据的查询能够得到快速处理。

请参见

数据库设计

索引

物理数据库文件和文件组

RAID


维护

数据库创建完成后，所有的对象和数据均已添加且都在使用中，有时必须对其进行维护。例如，定期备份数据库是很重要的。还可能需要创建一些新索引以提高性能。在设计数据库时，应考虑这些问题以将对用户的影响、执行任务的时间和所付出的努力降低到最低。

维护设计准则包括：

  • 将数据库设计得尽可能小，并排除冗余信息。

    规范化数据库有助于达到此目的。例如，减少数据库的大小可以帮助减少备份的时间，更重要的是减少恢复数据库的时间。在恢复操作期间，这显得尤为重要，因为数据库在恢复时不可用。

  • 如果表将包含大量的行，则应设计分离的表而不是单个表。

    例如，可以将含有银行接收的每个信用卡事务的表分成多个表，其中，每个表都含有一个月的数据。如果在有些情况下，将不得不添加新索引以提高查询性能，那么这样做可以减轻索引维护的负担。可能只有必要对最近三个月的数据创建索引，因为老数据将不再引用。表越大，创建新索引的时间越长。

Microsoft® SQL Server™ 2000 提供数据库维护计划向导以对这些任务中的许多进行自动处理，从而减少或消除了数据库维护所涉及的工作。

请参见

数据库维护计划向导


估计数据库的大小

在设计数据库时，可能需要估计填入数据时的数据库大小。估计数据库的大小有助于确定下列各项所需要的硬件配置：

  • 达到应用程序的性能要求。有关更多的信息，请参见数据库性能中的"硬件考虑事项"。

  • 确保有适当的物理磁盘空间以存储数据和索引。

估计数据库的大小还可以帮助您确定数据库设计是否需要进行精简处理。例如，可能认为数据库的估计值太大，无法在单位中实现，因此需要进行更多的规范化处理。相反，估计值可能比所期望的更小，这样就可以降低数据库的规范化程度以提高查询性能。

若要估计数据库的大小，请分别估计每个表的大小，然后累加所得的值。表的大小取决于表是否有索引，如果有，还取决于是哪种类型的索引。

请参见

设计表

索引

查询优化

表和索引构架


估计表的大小

下列步骤可用于估计存储表中的数据所需的空间量。

  1. 指定表中的行数：

    表中的行数 = Num_Rows

  2. 如果在表的定义中有固定长度和可变长度列，请计算数据行中这两组列的每一组所占用的空间。列的大小取决于数据类型和长度说明。有关更多信息，请参见数据类型。

    列数 = Num_Cols

    所有固定长度列中的字节总和 = Fixed_Data_Size

    可变长度列数 = Num_Variable_Cols

    所有可变长度列的最大值 = Max_Var_Size

  3. 如果表中有固定长度列，行的一部分（称为空位图）将保留以管理列的可为空性。计算大小：

    空位图 (Null_Bitmap) = 2 + (( Num_Cols + 7) / 8 )

    仅使用上述表达式中的整数部分，而去掉其余部分。

  4. 如果表中有可变长度列，请确定在行中存储这些列需使用的空间：

    可变长度列的总大小 (Variable_Data_Size) = 2 + (Num_Variable_Cols x 2) + Max_Var_Size

    如果没有可变长度列，请将 Variable_Data_Size 设置为 0。

    此公式假设所有可变长度列均百分之百充满。如果预计可变长度列占用的存储空间比例较低，则可以按照该比例调整结果以对整个表大小得出一个更准确的估计。

  5. 计算行大小：

    行总大小 (Row_Size) = Fixed_Data_Size + Variable_Data_Size + Null_Bitmap +4

    最后一个值 4 表示数据行首结构。

  6. 下一步，计算每页的行数（每页有 8096 可用字节）：

    每页的行数 (Rows_Per_Page) = ( 8096 ) / (Row_Size + 2)

    因为行不跨页，所以每页的行数应向下舌入到最接近的整数。

  7. 如果要在表上创建聚集索引，那么要根据指定的填充因子计算每页保留的可用行数。有关更多信息，请参见填充因子。如果不创建聚集索引，请将 Fill_Factor 指定为 100。

    每页的可用行数 (Free_Rows_Per_Page) = 8096 x ((100 - Fill_Factor) / 100) / (Row_Size + 2)

    计算中使用的填充因子为整数值，而不是百分数。

    因为行不跨页，所以每页的行数应向下舍入到最接近的整数。填充因子增大时，每页将存储更多的数据，因此页数将减少。

  8. 计算存储所有行所需的页数：

    页数 (Num_Pages) = Num_Rows / (Rows_Per_Page - Free_Rows_Per_Page)

    估计的页数应向上舍入到最接近的整数。

  9. 最后，计算存储表中的数据所需的空间量（每页总字节为8192）：

    表大小（字节）= 8192 x Num_Pages

请参见

设计表


估计无聚集索引的表的大小

下列步骤可用于估计存储没有聚集索引的表上的数据和任何附加的非聚集索引所需的空间。

  1. 计算存储数据所用的空间。

  2. 计算存储每个附加非聚集索引所用的空间。

  3. 汇总计算所得的值。

对于每个计算，都要指定将在表中出现的行数。表中的行数将对表的大小有直接影响：

表中的行数 = Num_Rows

计算存储数据所用的空间

若要计算存储数据所用的空间，请参见估计表的大小。

记下计算所得的值：

存储数据所用的空间 = Data_Space_Used

计算存储每个附加非聚集索引所用的空间

下列步骤可用于估计没有聚集索引的表上的单个非聚集索引的大小。

  1. 如果索引定义包含固定长度和可变长度列，请计算索引行中这两组列的每一组所占用的空间。列的大小取决于数据类型和长度说明。有关更多信息，请参见数据类型。

    索引键中的列数 = Num_Key_Cols

    所有固定长度键列中的字节总和 = Fixed_Key_Size

    索引键中的可变长度列数 = Num_Variable_Key_Cols

    所有可变长度键列的最大值 = Max_Var_Key_Size

  2. 如果索引中有固定长度列，那么索引行的一部分将为空位图保留。计算大小：

    索引空位图 (Index_Null_Bitmap) = 2 + (( Num_Key_Cols + 7) / 8 )

    仅使用上述表达式中的整数部分，而去掉其余部分。

  3. 如果索引中有可变长度列，请确定存储索引行中的这些列需使用的空间：

    可变长度列的总大小 (Variable_Key_Size) = 2 + (Num_Variable_Key_Cols x 2) + Max_Var_Key_Size

    如果没有可变长度列，请将 Variable_Key_Size 设置为 0。

    此公式假设所有可变长度键列均百分之百充满。如果预计可变长度键列占用的存储空间比例较低，则可以按照该比例调整结果以对整个索引大小得出一个更准确的估计。

  4. 计算索引行大小：

    索引行总大小 (Index_Row_Size) = Fixed_Key_Size + Variable_Key_Size + Index_Null_Bitmap + 1 + 8

  5. 下一步，计算每页的索引行数（每页有 8096 个可用字节）：

    每页的索引行数 (Index_Rows_Per_Page) = ( 8096 ) / (Index_Row_Size + 2)

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  6. 根据为非聚集索引指定的填充因子，计算每叶级页保留的可用索引行数。有关更多信息，请参见填充因子。

    每叶级页的可用索引行数 (Free_Index_Rows_Per_Page) = 8096 x ((100 - Fill_Factor) / 100) /
    Index_Row_Size

    计算中使用的填充因子为整数值，而不是百分数。

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  7. 下一步，计算存储索引的每一级别的所有索引行所需的页数：

    页数（第 0 级） (Num_Pages_Level_0) = Num_Rows / (Index_Rows_Per_Page - Free_Index_Rows_Per_Page)

    页数（第 1 级） (Num_Pages_Level_1) = Num_Pages_Level_0 / Index_Rows_Per_Page

    重复第二个计算，将从前面的第 n 级中计算的页数除以 Index_Rows_Per_Page，直到指定的第 n (Num_Pages_Level_n) 级页数等于 1（根页）。例如，若要计算第二个索引级别所需的页数：

    页数（第 2 级） (Num_Pages_Level_2) = Num_Pages_Level_1 / Index_Rows_Per_Page

    对于每一级别，预计的页数应向上舍入到最接近的整数。

    汇总存储各索引级别所需页数：

    总页数 (Num_Index_Pages) = Num_Pages_Level_0 + Num_Pages_Level_1 +Num_Pages_Level_2 + ...+ Num_Pages_Level_n

  8. 计算聚集索引的大小（每页总共有 8192 个字节）：

    非聚集索引大小（字节） = 8192 x Num_Index_Pages

计算表的大小

计算表的大小：

表的总大小（字节）= Data_Space_Used + Nonclustered index size + ...n

请参见

创建索引

非聚集索引


估计带有聚集索引的表的大小

下列步骤可用于估计存储带有聚集索引的表上的数据和任何附加的非聚集索引所需的空间。

  1. 计算存储数据所用的空间。

  2. 计算存储聚集索引所用的空间。

  3. 计算存储每个附加非聚集索引所用的空间。

  4. 汇总计算所得的值。

对于每个计算，都要指定将在表中出现的行数。表中的行数将对表的大小有直接影响：

表中的行数 = Num_Rows

计算存储数据所用的空间

有关如何计算存储数据所用空间的更多信息，请参见估计表的大小。

记下计算所得的值：

存储数据所用的空间 = Data_Space_Used

计算存储聚集索引所用的空间

下列步骤可用于估计存储聚集索引所需的空间。

  1. 聚集索引定义可以包括固定长度和可变长度列。为了估计聚集索引的大小，需要指定索引行中这两组列的每一组所占用的空间。

    索引键中的列数 = Num_CKey_Cols

    所有固定长度键列中的字节总和 = Fixed_CKey_Size

    索引键中的可变长度列数 = Num_Variable_CKey_Cols

    所有可变长度键列的最大值 = Max_Var_CKey_Size

  2. 如果聚集索引中有固定长度列，那么索引行的一部分将为空位图保留。计算大小：

    索引空位图 (CIndex_Null_Bitmap) = 2 + (( Num_CKey_Cols + 7) / 8 )

    仅使用上述表达式中的整数部分，而去掉其余部分。

  3. 如果索引中有可变长度列，请确定存储索引行中的这些列需使用的空间：

    可变长度列的总大小 (Variable_CKey_Size) = 2 + (Num_Variable_CKey_Cols x 2) + Max_Var_CKey_Size

    如果没有可变长度列，请将 Variable_CKey_Size 设置为 0。

    此公式假设所有可变长度键列均百分之百充满。如果预计可变长度键列占用的存储空间比例较低，则可以按照该比例调整结果以对整个索引大小得出一个更准确的估计。

  4. 计算索引行大小：

    索引行总大小 (CIndex_Row_Size) = Fixed_CKey_Size + Variable_CKey_Size + CIndex_Null_Bitmap + 1 + 8

  5. 下一步，计算每页的索引行数（每页有 8096 个可用字节）：

    每页的索引行数 (CIndex_Rows_Per_Page) = ( 8096 ) / (CIndex_Row_Size + 2)

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  6. 下一步，计算存储索引的每一级别的所有索引行所需的页数。

    页数（第 0 级）(Num_Pages_CLevel_0) = (Data_Space_Used / 8192) / CIndex_Rows_Per_Page

    页数（第 1 级）(Num_Pages_CLevel_1) = Num_Pages_CLevel_0 / CIndex_Rows_Per_Page

    重复第二个计算，将从前面的第 n 级中计算的页数除以 CIndex_Rows_Per_Page，直到指定的第 n (Num_Pages_CLevel_n) 级页数等于 1（索引根页）。例如，若要计算第二个索引级别所需的页数：

    页数（第 2 级） (Num_Pages_CLevel_2) = Num_Pages_CLevel_1 / CIndex_Rows_Per_Page

    对于每一级别，预计的页数应向上舍入到最接近的整数。

    汇总存储各索引级别所需页数：

    总页数 (Num_CIndex_Pages) = Num_Pages_CLevel_0 + Num_Pages_CLevel_1 +
    Num_Pages_CLevel_2 + ...+ Num_Pages_CLevel_n

  7. 计算聚集索引的大小（每页总共有 8192 字节）：

    聚集索引大小（字节） = 8192 x Num_CIndex_Pages

计算存储每个附加非聚集索引所用的空间

下列步骤可用于估计存储每个附加的非聚集索引所需空间量。

  1. 非聚集索引定义可以包括固定长度和可变长度列。为了估计非聚集索引的大小，需要计算索引行中这两组列的每一组所占用的空间。

    索引键中的列数 = Num_Key_Cols

    所有固定长度键列中的字节总和 = Fixed_Key_Size

    索引键中的可变长度列数 = Num_Variable_Key_Cols

    所有可变长度键列的最大值 = Max_Var_Key_Size

  2. 如果索引中有固定长度列，那么索引行的一部分将为空位图保留。计算大小：

    索引空位图 (Index_Null_Bitmap) = 2 + (( Num_Key_Cols + 7) / 8 )

    仅使用上述表达式中的整数部分，而去掉其余部分。

  3. 如果索引中有可变长度列，请确定存储索引行中的这些列需使用的空间：

    可变长度列的总大小 (Variable_Key_Size) = 2 + (Num_Variable_Key_Cols x 2) + Max_Var_Key_Size

    如果没有可变长度列，请将 Variable_Key_Size 设置为 0。

    此公式假设所有可变长度键列均百分之百充满。如果预计可变长度键列占用的存储空间比例较低，则可以按照该比例调整结果以对整个索引大小得出一个更准确的估计。

  4. 计算非叶级索引行大小：

    非叶级索引行总大小 (NL_Index_Row_Size) = Fixed_Key_Size + Variable_Key_Size + Index_Null_Bitmap + 1 + 8

  5. 计算每页的非叶级索引行数：

    每页的非叶级索引行数 (NL_Index_Rows_Per_Page) =
    ( 8096 ) / (NL_Index_Row_Size + 2)

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  6. 计算叶级索引行大小：

    叶级索引行总大小 (Index_Row_Size) = CIndex_Row_Size + Fixed_Key_Size + Variable_Key_Size + Index_Null_Bitmap + 1

    最后一个值 1 表示索引行首结构。CIndex_Row_Size 是聚集索引键的索引行总大小。

  7. 计算每页的叶级索引行数：

    每页的叶级索引行数 (Index_Rows_Per_Page) = ( 8096 ) / (Index_Row_Size + 2)

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  8. 根据为非聚集索引指定的填充因子，计算每页保留的可用索引行数。有关更多信息，请参见填充因子。

    每页的可用索引行数 (Free_Index_Rows_Per_Page) = 8096 x ((100 - Fill_Factor) / 100) / Index_Row_Size

    计算中使用的填充因子为整数值，而不是百分数。

    由于索引行不能跨页，所以每页的索引行数应向下舍入到最接近的整数。

  9. 计算存储索引的每一级别的所有索引行所需的页数：

    页数（第 0 级） (Num_Pages_Level_0) = Num_Rows / (Index_Rows_Per_Page - Free_Index_Rows_Per_Page)

    页数（第 1 级） (Num_Pages_Level_1) = Num_Pages_Level_0 / NL_Index_Rows_Per_Page

    重复第二个计算，将从前面的第 n 级中计算的页数除以 NL_Index_Rows_Per_Page，直到指定的第 n (Num_Pages_Level_n) 级页数等于 1（根页）。

    例如，若要计算第二个和第三个索引级别所需页数：

    数据页数（第 2 级） (Num_Pages_Level_2) = Num_Pages_Level_1 / NL_Index_Rows_Per_Page

    数据页数（第 3 级） (Num_Pages_Level_3) = Num_Pages_Level_2 / NL_Index_Rows_Per_Page

    对于每一级别，预计的页数应向上舍入到最接近的整数。

    汇总存储各索引级别所需页数：

    总页数 (Num_Index_Pages) = Num_Pages_Level_0 + Num_Pages_Level_1 +Num_Pages_Level_2 + ...+ Num_Pages_Level_n

  10. 计算非聚集索引的大小：

    非聚集索引大小（字节） = 8192 x Num_Index_Pages

计算表的大小

计算表的大小：

  • 表的总大小（字节） = Data_Space_Used + Clustered index size + Nonclustered index size + ...n

请参见

聚集索引

创建索引

非聚集索引


创建数据库

若要创建数据库，必须先确定数据库的名称、所有者（创建数据库的用户）、大小，以及用于存储该数据库的文件和文件组。

在创建数据库之前，请考虑如下事项：

  • 创建数据库的权限默认授予 sysadmin 和 dbcreator 固定服务器角色的成员，但是它仍可以授予其他用户。

  • 创建数据库的用户将成为该数据库的所有者。

  • 在一个服务器上，最多可以创建 32,767 个数据库。

  • 数据库名称必须遵循标识符规则。

有三种文件类型可用于存储数据库：

  • 主文件

    这些文件包含数据库的启动信息。主文件还用于存储数据。每个数据库都包含一个主文件。

  • 次要文件

    这些文件含有不能置于主要数据文件中的所有数据。如果主文件足够大，能够容纳数据库中的所有数据，则该数据库不需要次要数据文件。有些数据库可能非常大，因此需要多个次要数据文件，或可能在各自的磁盘驱动器上使用次要文件，以便在多个磁盘上存储数据。

  • 事务日志

    这些文件包含用于恢复数据库的日志信息。每个数据库必须至少有一个事务日志文件（但是可以有多个）。日志文件最小为 512 KB。

    重要 Microsoft® SQL Server™ 2000 数据和事务日志文件不能放在压缩文件系统或远程网络驱动器上（如共享的网络目录）。

创建数据库之后，构成该数据库的所有文件都将用零填充，以重写磁盘上以前的删除文件所遗留的现有数据。虽然这意味着创建文件所需的时间更长，但在完成此操作之后，在正常的数据库操作过程中，当数据第一次写入到文件中时，操作系统将无须再用零填充这些文件。这提高了日常操作的性能。

建议您指定文件的最大允许增长的大小。这样做可以防止文件在添加数据时无限制增大，以至用尽整个磁盘空间。若要指定文件的最大大小，请在使用SQL Server 企业管理器内的"属性"对话框创建数据库时，使用 CREATE DATABASE 语句的 MAXSIZE 参数或者"将文件增长限制为 (MB)"选项。

创建数据库之后，建议创建一个 master 数据库的备份。

创建数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

用创建数据库向导创建数据库

企业管理器

企业管理器

请参见

更改数据库所有者

文件和文件组

使用标识符

sp_helpfile

sp_helpfilegroup


使用原始分区

Microsoft® SQL Server™ 2000 支持使用原始分区创建数据库文件。原始分区是指没有用 Windows NT® 文件系统（如 FAT 和 NTFS）格式化的磁盘分区。在某些情况下，使用在原始分区上创建的数据库，可以获得比在 NTFS 或 FAT 上更高的性能。但是，对于大多数安装，首选方法是使用在 NTFS 或 FAT 分区上创建的文件。

在原始分区上创建数据库文件时，不必指定构成该数据库的文件的物理名称，只需指定要在其上创建数据库文件的磁盘的驱动器号。

如果正在使用 Microsoft Windows® 2000 Server，则可以创建指向原始分区的已装入驱动器。在空文件夹中装入一个本地驱动器时，Windows 2000 将给该驱动器分配驱动器路径而不是驱动器号。已装入驱动器不受驱动器号强加的 26 个驱动器的限制，因此可以使用任意多个原始分区。在已装入驱动器上创建数据库文件时，必须在文件名后尾随一个反斜线符号来结束驱动器路径，例如，E:\Sample name\。有关创建已装入驱动器的信息，请参见 Windows 2000 Server 文档。

在使用原始分区时，需考虑如下限制：

  • 每个原始分区上只能创建一个数据库文件。逻辑分区必须配置为一个单一的数据库文件，因为原始分区上没有文件系统。

  • 常规文件系统操作（如复制、移动和删除等）不能在原始分区上使用。

  • 原始分区上的数据库文件不能用 Windows NT 备份实用工具备份。但是，仍可创建 SQL Server 数据库备份或事务日志备份。

  • 原始分区上的数据库文件不能自动扩充。或者最初就以其实际大小创建数据库，或者手动扩充数据库文件。有关更多信息，请参见扩充数据库。

  • 只能使用字母编号的分区（例如 E:）或已装入的驱动器（例如 E:\Sample name\）。不能使用数字编号的设备。

  • 文件系统服务（例如坏块替换）在原始分区上不可用。

请参见

CREATE DATABASE


修改数据库

创建数据库后，可以对其原始定义进行更改。更改包括：

  • 扩充分配给数据库的数据或事务日志空间。

  • 收缩分配给数据库的数据或事务日志空间。

  • 添加或删除数据和事务日志文件。

  • 创建文件组。

  • 创建默认文件组。

  • 更改数据库的配置设置。

  • 脱机放置数据库。

  • 附加新数据库或分离未使用的数据库。

  • 更改数据库名称。

  • 更改数据库的所有者。

在更改数据库之前，有时需要使数据库退出正常操作模式。在这些情况下，需确定终止事务的适当方法。


扩充数据库

Microsoft® SQL Server™ 2000 可根据在创建数据库时所定义的增长参数，自动扩充数据库。通过在现有的数据库文件上分配其它的文件空间，或者在另一个新文件上分配空间，还可以手动扩充数据库。如果现有的文件已经充满，则可能需要扩充数据或事务日志空间。如果数据库已经用完分配给它的空间而又不能自动增长，则会出现 1105 错误。

扩充数据库时，必须按至少 1 MB 增加该数据库的大小。扩充数据库的权限默认授予数据库所有者，并自动与数据库所有者身份一起传输。数据库扩充后，数据或事务日志文件立即可以使用新空间，这取决于哪个文件进行了扩充。

如果事务日志没有设置为自动扩充，则当数据库内发生某些类型的活动时，该事务日志可能会用完所有空间。备份事务日志时，或者在数据库使用简单恢复模型的每个检查点时，只清除事务日志中非活动（已提交）的部分。然后 SQL Server 可以重新使用该事务日志中被截取的、尚未使用的部分。有关截断事务日志的更多信息，请参见截断事务日志。

在备份数据库时，SQL Server 不会截断事务日志。

在扩充数据库时，建议指定文件的最大允许增长的大小。这样做可以防止文件无限制地增大，以至用尽整个磁盘空间。若要指定文件的最大大小，请在使用SQL Server 企业管理器内的"属性"对话框创建数据库时，使用 CREATE DATABASE 语句的 MAXSIZE 参数或者"将文件增长限制为 (MB)"选项。

扩充数据库以增大数据或事务日志空间的过程完全相同。

扩充 tempdb

默认情况下，在需要空间时 tempdb 数据库会自动增长，因为文件的 MAXSIZE 设置为 UNLIMITED。因此，tempdb 可以一直增长，直至用完包含 tempdb 的磁盘上的所有空间。若要防止 tempdb 无限制地增长，请使用 ALTER DATABASE 语句或 SQL Server 企业管理器为 tempdb 设置 MAXSIZE。

相反，如果 tempdb 已经设置了 MAXSIZE，而又需要增加 tempdb 的大小，则必须执行以下操作之一：

  • 增加 tempdb 当前使用的默认文件组中文件的大小。

  • 将新文件添加到默认文件组。

  • 允许 tempdb 使用的文件自动增长。

重要 用户定义的文件组不能与 tempdb 一起使用。它们只能与默认文件组一起使用。

移动 tempdb

更改 tempdb 数据库的物理位置：

  1. 使用 ALTER DATABASE 语句和 MODIFY FILE 子句更改 tempdb 数据库，以更改 tempdb 中每个文件的物理文件名，使之引用新的物理位置（例如新磁盘）。

  2. 停止并重新启动 SQL Server。

  3. 从原始位置删除旧的 tempdb 数据库文件。

增加数据库的大小

Transact-SQL

企业管理器

企业管理器

更改数据库的物理位置

Transact-SQL

请参见

ALTER DATABASE

更改默认文件组

使用文件和文件组管理数据库增长


收缩数据库

Microsoft® SQL Server™ 2000 允许收缩数据库中的每个文件以删除未使用的页。数据和事务日志文件都可以收缩。数据库文件可以作为组或单独地进行手工收缩。数据库也可设置为按给定的时间间隔自动收缩。该活动在后台进行，并且不影响数据库内的用户活动。

当使用 ALTER DATABASE AUTO_SHRINK 选项（或 sp_dboption 系统存储过程）将数据库设置为自动收缩，且数据库中有足够的可用空间时，则会发生收缩。但是，如果不能配置要删除的可用空间的百分比，则将删除尽可能多的可用空间。若要配置将删除的可用空间量，例如只删除数据库中当前可用空间的 50%，请使用SQL Server 企业管理器内的"属性"对话框进行数据库收缩。

不能将整个数据库收缩到比其原始大小还要小。因此，如果数据库创建时的大小为 10 MB，后来增长到 100 MB，则该数据库最小能够收缩到 10 MB（假定已经删除该数据库中所有数据）。

但是，使用 DBCC SHRINKFILE 语句，可以将单个数据库文件收缩到比其初始创建大小还要小。必须分别收缩每个文件，而不要试图收缩整个数据库。

事务日志文件可在固定的边界内收缩。虚拟日志的大小决定可能减小的大小。因此，不能将日志文件收缩到比虚拟日志文件还小。另外，日志文件可以按与虚拟日志文件的大小相等的增量收缩。例如，一个初始大小为 1 GB 的较大事务日志文件可以包括五个虚拟日志文件（每个文件大小为 200 MB）。收缩事务日志文件将删除未使用的虚拟日志文件，但会留下至少一个虚拟日志文件。因为此示例中的每个虚拟日志文件都是 200 MB，所以事务日志最小只能收缩到 200 MB，且每次只能以 200 MB的大小收缩。若要让事务日志文件收缩得更小，可以创建一个更小的事务日志，并允许它自动增长，而不要创建一个较大的事务日志文件。

在 SQL Server 2000 中，DBCC SHRINKDATABASE 或 DBCC SHRINKFILE 操作试图立即将事务日志文件收缩到所要求的大小（以四舍五入的值为准）。在收缩文件之前应截断日志文件，以减小逻辑日志的大小并将其标记为不包含逻辑日志任何部分的不活动的虚拟日志。有关更多信息，请参见收缩事务日志。

说明 不可能在备份数据库或事务日志时收缩数据库或事务日志。反之，也不可能在收缩数据库或事务日志时创建数据库或事务日志备份。

收缩数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

将数据库设置为自动收缩

Transact-SQL

企业管理器

企业管理器

SQL-DMO

收缩数据库文件

Transact-SQL

SQL-DMO

请参见

虚拟日志文件


添加和删除数据和事务日志文件

可以添加数据和事务日志文件以扩充数据库，或删除它们以收缩数据库。当添加文件时，数据库可以立即使用该文件。

重要 不能将 Microsoft® SQL Server™ 2000 数据和事务日志文件放在压缩的文件系统上。

SQL Server 在每个文件组内的所有文件上使用按比例填充的策略，根据文件中的可用空间按比例写入数据量，并允许立即开始使用新文件。通过这种方式，所有文件几乎在同一时间趋于充满。但是，事务日志文件不能作为文件组的一部分；它们是相互独立的。当事务日志增长时，使用填充后往下走的策略，而不是按比例填充策略，首先填充第一个日志文件，然后是第二个，依此类推。因此，当添加日志文件时，事务日志可能不能使用该文件，而需等到其它文件已经先填充。

将文件添加到数据库时，可以根据需要指定文件的大小（默认为 1 MB）、文件可以增长的最大大小（如果文件内的空间用完）、文件每次需要增长时所增长的数量（默认是 10%），以及文件所属的文件组。

删除数据或事务日志文件将从数据库删除该文件。仅当文件上不存在已有的数据或事务日志信息时才可能从数据库删除文件；文件必须完全为空后才能删除。若要将数据从一个数据文件迁移到同一文件组中的其它文件中，请使用 DBCC SHRINKFILE 语句，并指定 EMPTYFILE 子句。SQL Server 即不再允许将数据置于文件上，从而通过使用 ALTER DATABASE 语句或 SQL Server 企业管理器内的属性页，使之能够删除。

通过将事务日志数据从一个日志文件迁移到另一个以删除事务日志文件是不可能的。若要从事务日志文件清除非活动的事务，必须截断或备份该事务日志。一旦事务日志文件不再包含任何活动或不活动的事务，该日志文件就可以从数据库中删除。

重要 添加或删除文件后，请立即创建数据库备份。在创建完整的数据库备份之前，不应该创建事务日志备份。

将数据或事务日志文件添加到数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

从数据库中删除数据或日志文件

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

物理数据库文件和文件组

sp_helpfile

使用文件和文件组管理数据库增长


创建文件组

在首次创建数据库，或者以后将更多文件添加到数据库时，可以创建文件组。但是，一旦将文件添加到数据库，就不可能再将这些文件移到其它文件组。

一个文件不能是多个文件组的成员。表格、索引以及 text、ntext 和 image 数据可以与特定的文件组相关联。这意味着它们的所有页都将从该文件组的文件中分配。

有三种类型的文件组：

  • 主文件组

    这些文件组包含主数据文件以及任何其它没有放入其它文件组的文件。系统表的所有页都从主文件组分配。

  • 用户定义文件组

    该文件组是用 CREATE DATABASE 或 ALTER DATABASE 语句中的 FILEGROUP 关键字，或在 SQL Server 企业管理器内的"属性"对话框上指定的任何文件组。

  • 默认文件组

    默认文件组包含在创建时没有指定文件组的所有表和索引的页。在每个数据库中，每次只能有一个文件组是默认文件组。如果没有指定默认文件组，则默认文件组是主文件组。

最多可以为每个数据库创建 256 个文件组。文件组只能包含数据文件。事务日志文件不能是文件组的一部分。

说明 文件组不能独立于数据库文件创建。文件组是在数据库中对文件进行分组的一种管理机制。

创建数据库时添加文件组

Transact-SQL

企业管理器

企业管理器

SQL-DMO

将文件组添加到数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

物理数据库文件和文件组

sp_helpfilegroup

sp_helpfile

使用文件和文件组管理数据库增长


更改默认文件组

更改默认文件组时，最初没有指定文件组的所有对象都被分配到新默认文件组中的数据文件。更改默认文件组可以防止不是专门在用户定义的文件组上创建的用户对象与系统对象和表竞争以获得数据空间。

更改默认文件组

Transact-SQL

SQL-DMO

请参见

默认文件组


设置数据库选项

可以为每个数据库都设置若干个决定数据库特点的数据库级选项。只有系统管理员、数据库所有者以及 sysadmin 和 dbcreator 固定服务器角色和 db_owner 固定数据库角色的成员才能修改这些选项。这些选项对于每个数据库都是唯一的，而且不影响其它数据库。可以使用 ALTER DATABASE 语句的 SET 子句、sp_dboption 系统存储过程，或者在某些情况下使用 SQL Server 企业管理器设置数据库选项。

说明 通过使用 sp_configure 系统存储过程或者 SQL Server 企业管理器，可以设置服务器范围的设置。有关更多信息，请参见设置配置选项。使用 SET 语句指定连接级设置。有关更多信息，请参见 SET 选项。

设置了数据库选项之后，将自动发出一个检查点，它会使修改立即生效。

若要更改新创建数据库的任意数据库选项的默认值，请更改 model 数据库中的适当数据库选项。例如对于随后创建的任何新数据库，如果希望 AUTO_SHRINK 数据库选项的默认设置都为 ON，则将 model 的 AUTO_SHRINK 选项设置为 ON。

有五类数据库选项：

  • 自动选项

  • 游标选项

  • 恢复选项

  • SQL 选项

  • 状态选项
自动选项

自动选项控制某些自动行为。

AUTO_CLOSE

当设置为 ON 时，在数据库的最后一个用户退出，而且数据库中的所有过程都完成时，数据库将关闭并完全退出系统，从而释放所有资源。默认情况下，当使用 Microsoft® SQL Server™ 2000 Desktop Engine 时，对于所有数据库，此选项都设置为 ON，而对于所有其它版本都设置为 OFF，与操作系统无关。当用户试图再次使用数据库时，该数据库将自动重新打开。如果数据库完全退出系统，则该数据库不会重新打开，直到下一次 SQL Server 重新启动时，用户试图使用该数据库为止。当为 OFF 时，即使当前没有用户使用数据库，数据库仍然保持打开状态。

AUTO_CLOSE 选项对于桌面数据库很有用，因为它允许将数据库文件作为常规文件进行管理。它们可以移动、复制以制作备份，或者甚至通过电子邮件发送给其他用户。如果应用程序与 SQL Server 反复建立连接和断开连接，则不应对这样的应用程序所访问的数据库使用 AUTO_CLOSE 选项。在每个连接之间关闭和重新打开数据库的开销将削弱性能。

此选项的状态可以通过检查 DATABASEPROPERTYEX 函数的 IsAutoClose 属性来确定。

AUTO_CREATE_STATISTICS

当设置为 ON 时，将在谓词中使用的列上自动创建统计。添加统计将提高查询性能，因为 SQL Server 查询优化器可以更好地确定如何估算查询。如果未使用统计，则 SQL Server 将自动删除它们。当设置为 OFF 时，SQL Server 不自动创建统计；相反，可以手动创建统计。有关更多信息，请参见统计信息。

默认情况下，AUTO_CREATE_STATISTICS 为 ON。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAutoCreateStatistics 属性来确定。

AUTO_UPDATE_STATISTICS

当设置为 ON 时，因为更改表中数据而造成统计过期时，将自动更新现有的统计。当设置为 OFF 时，现有的统计不会自动更新；而可以手动更新该统计。有关更多信息，请参见统计信息。

默认情况下，将 AUTO_UPDATE_STATISTICS 设置为 ON。

此选项的状态可以通过检查 DATABASEPROPERTYEX 函数的 IsAutoUpdateStatistics 属性来确定。

AUTO_SHRINK

当设置为 ON 时，数据库文件可作为定期收缩的对象。数据文件和日志文件都可以自动由 SQL Server 收缩。当设置为 OFF 时，在定期检查未使用空间的过程中，数据库文件不自动收缩。默认情况下，当使用 SQL Server 桌面版时，对于所有数据库，此选项都设置为 ON，而对于所有其它版本都设置为 OFF，与操作系统无关。

如果将数据库设置为 SIMPLE 恢复模型或备份日志，则 AUTO_SHRINK 只减小事务日志的大小。

当文件中超过百分之二十五的部分包含未使用的空间时，AUTO_SHRINK 选项将导致收缩文件。文件将收缩至未使用空间占文件 25% 的大小，或收缩至文件创建时的大小，两者之间取其大。

不能收缩只读数据库。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAutoShrink 属性来确定。

游标选项

游标选项控制游标的行为和作用域。

CURSOR_CLOSE_ON_COMMIT

当设置为 ON 时，在提交事务时，所有打开的游标都将自动关闭（遵从 SQL-92 规范）。默认情况下，此设置为 OFF，并且游标仍然在各事务边界间保持打开状态，仅当连接关闭或被显式关闭时，才会关闭游标。

连接级设置（使用 SET 语句设置）替代 CURSOR_CLOSE_ON_COMMIT 的默认数据库设置。默认情况下，当连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，以将会话的 CURSOR_CLOSE_ON_COMMIT 设置为 OFF。有关更多信息，请参见 SET CURSOR_CLOSE_ON_COMMIT。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsCloseCursorsOnCommitEnabled 属性来确定。

CURSOR_DEFAULT LOCAL | GLOBAL

如果设置了 CURSOR_DEFAULT LOCAL，且创建游标时没有将其定义为 GLOBAL，则对于在其中创建游标的批处理、存储过程或触发器，游标的作用域都是局部。游标名仅在该作用域内有效。在批处理、存储过程、触发器或存储过程 OUTPUT 参数中，该游标可由局部游标变量引用。当批处理、存储过程或触发器终止时，游标将被隐式释放，除非它在一个 OUTPUT 参数中传递回来。如果 OUTPUT 参数将游标传递回来，则游标在最后引用它的变量释放或离开作用域时释放。

如果设置了 CURSOR_DEFAULT GLOBAL，且在创建游标时没有将其定义为 LOCAL，则游标的作用域对于连接是全局的。在由连接执行的任何存储过程或批处理中，都可以引用该游标名称。该游标仅在断开时被隐式释放。CURSOR_DEFAULT GLOBAL 是默认设置。有关更多信息，请参见 DECLARE CURSOR。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsLocalCursorsDefault 属性来确定。

恢复选项

恢复选项控制数据库的恢复模型。

RECOVERY FULL | BULK_LOGGED | SIMPLE

指定 FULL 时，数据库备份和事务日志备份用于提供从媒体故障中完全恢复的能力。包括大容量操作（如 SELECT INTO、CREATE INDEX 和大容量装载数据）在内的所有操作都将完整记入日志。有关更多信息，请参见完全恢复。

指定 BULK_LOGGED 时，对所有 SELECT INTO、CREATE INDEX 和大容量装载数据操作的日志记录是最小的，因此所需的日志空间较少。这种模式可获得更好的性能和更少的日志空间使用量，但是丢失数据的危险比使用完整恢复要大。有关更多信息，请参见大容量日志记录恢复。

指定 SIMPLE 时，只能将数据库恢复到上一次完整数据库备份或上一次差异备份。有关更多信息，请参见简单恢复。

SIMPLE 是 SQL Server 桌面版和数据引擎的默认设置，FULL 是所有其它版本的默认设置。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 Recovery 属性来确定。

TORN_PAGE_DETECTION

此恢复选项允许 SQL Server 检测由于电源故障或其它系统停歇导致的未完成的 I/O 操作。

当设置为 ON 时，只要一个 8 KB 的数据库页写入磁盘，该页的每个 512 字节扇区都有一位被翻转。当 SQL Server 以后读取该页时，如果有一个位发生错误，则该页将被错误写入；这样就会检测到一个残缺页。通常在恢复期间检测到残缺页，因为错误写入的任何页都可能在恢复时被读取。

虽然 SQL Server 数据库页是 8 KB，但是磁盘使用 512 字节的扇区执行 I/O 操作。因此，每个数据库页要写 16 个扇区。如果在操作系统将第一个 512 字节扇区写到磁盘和完成 8 KB I/O 操作之间系统失败（例如，由于电源故障），则可能出现残缺页。如果数据库页的第一个扇区在失败之前成功地写入磁盘，则磁盘上的数据库页将显示为更新过的，尽管可能没有更新成功。

说明 使用备用电池的磁盘高速缓存可以保证数据成功地写到磁盘上，或者根本就不写。

如果检测到残缺页，将出现一个 I/O 错误，并断开连接。如果在恢复过程中检测到残缺页，则数据库也会被标记为可疑。应该还原数据库备份，并应用所有事务日志备份，因为它在物理上是不一致的。

默认情况下，TORN_PAGE_DETECTION 为 ON。

此选项的当前设置可通过检查 DATABASEPROPERTYEX 的 IsTornPageDetectionEnabled 属性来确定。

SQL 选项

SQL 选项控制 ANSI 遵从选项。

ANSI_NULL_DEFAULT

允许用户控制数据库默认为空。未显式指定 NULL 或 NOT NULL 时，用户定义的数据类型或列定义将使用为空性的默认设置。为空性由会话或数据库设置确定。Microsoft® SQL Server™ 2000 默认为 NOT NULL。对于 ANSI 兼容性，数据库选项 ANSI_NULL_DEFAULT 设置为 ON 将使数据库默认设置更改为 NULL。

当此选项设置为 ON 时，在 CREATE TABLE 或 ALTER TABLE 语句过程中，没有显式定义为 NOT NULL 的所有用户定义的数据类型或列都将默认为允许空值。使用约束定义的列都将遵循约束规则，而无视此设置。

连接级设置（使用 SET 语句设置）替代 ANSI_NULL_DEFAULT 的默认数据库级设置。默认情况下，当连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，以将会话的 ANSI_NULL_DEFAULT 设置为 ON。有关更多信息，请参见 SET ANSI_NULL_DFLT_ON。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAnsiNullDefault 属性来确定。

ANSI_NULLS

当设置为 ON 时，所有与空值比较的值都取值为 NULL（未知）。当设置为 OFF 时，如果两个值都为 NULL，则非 Unicode 值与空值比较的值都取值为 TRUE。默认情况下，ANSI_NULLS 数据库选项为 OFF。

连接级设置（使用 SET 语句设置）替代 ANSI_NULLS的默认数据库设置。默认情况下，当连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，将会话的 ANSI_NULLS 设置为 ON。有关更多信息，请参见 SET ANSI_NULLS。

在计算列或索引视图上创建或操作索引时，SET ANSI_NULLS 也必须为 ON。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAnsiNullsEnabled 属性来确定。

ANSI_PADDING

当设置为 ON 时，不剪裁字符值中插入到 varchar 列的尾随空格和二进制值中插入到 varbinary 列的尾随零。不将值按列的长度进行填充。当设置为 OFF 时，剪裁 varchar 列的尾随空格和 varbinary 列的尾随零。该设置只影响新列的定义。

SET ANSI_PADDING 为 ON 时，将允许空值的 Char(n) 和 binary(n) 列填充到列长，而当 SET ANSI_PADDING 为 OFF 时，将剪裁尾随空格和零。始终将不允许空值的 Char(n) 和 binary(n) 列填充到列长。

重要 建议始终将 ANSI_PADDING 设置为 ON。在计算列或索引视图上创建或操作索引时，SET ANSI_PADDING 必须为 ON。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAnsiPaddingEnabled 属性来确定。

ANSI_WARNINGS

当设置为 ON 时，在出现如"被零除"或聚合函数中出现空值这类情形时，将发出错误或警告。当设置为 OFF 时，聚合函数中出现空值时不会发出警告，而在出现"被零除"这类情形时将返回空值。默认情况下，ANSI_WARNINGS 为 OFF。

在计算列或索引视图上创建或操作索引时，SET ANSI_WARNINGS 必须设置为 ON。

连接级设置（使用 SET 语句设置）替代 ANSI_WARNINGS 的默认数据库设置。默认情况下，当连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，将会话的 ANSI_WARNINGS 设置为 ON。有关更多信息，请参见 SET ANSI_WARNINGS。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsAnsiWarningsEnabled 属性来确定。

ARITHABORT

当设置为 ON 时，溢出或被零除错误将导致查询或批处理终止。如果错误发生在事务内，则回滚事务。当设置为 OFF 时，如果出现其中一个错误则显示警告信息，而查询、批处理或事务继续处理，就好象没有发生错误一样。

在计算列或索引视图上创建或操作索引时，SET ARITHABORT 必须设置为 ON。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsArithmeticAbortEnabled 属性来确定。

NUMERIC_ROUNDABORT

如果设置为 ON，则当表达式中出现精度损失时将生成错误。当设置为 OFF 时，精度损失不生成错误信息，并且将结果四舍五入为存储结果的列或变量的精度。

当在计算列或索引视图上创建或操作索引时，SET NUMERIC_ROUNDABORT 必须设置为 OFF。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsNumericRoundAbortEnabled 属性来确定。

CONCAT_NULL_YIELDS_NULL

当设置为 ON 时，如果串联操作中的某个操作数为 NULL，则操作结果为 NULL。例如，将字符串"This is"和 NULL 串联将得到 NULL 值，而不是值"This is"。

当为 OFF 时，将一个空值与一个字符串联在一起产生作为结果的字符串；该空值被视为空字符串进行处理。默认情况下，CONCAT_NULL_YIELDS_NULL 为 OFF。

当在计算列或索引视图上创建或操作索引时，SET CONCAT_NULL_YIELDS_NULL 必须设置为 ON。

连接级设置（使用 SET 语句设置）替代 CONCAT_NULL_YIELDS_NULL 的默认数据库设置。默认情况下，当连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，将会话的 CONCAT_NULL_YIELDS_NULL 设置为 ON。有关更多信息，请参见 SET CONCAT_NULL_YIELDS_NULL。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsNullConcat 属性来确定。

QUOTED_IDENTIFIER

当设置为 ON 时，可以用双引号分隔标识符，并且必须用单引号分隔文字。所有用双引号分隔的字符串都被解释为对象标识符。引用的标识符不必遵循用于标识符的 Transact-SQL 规则。它们可以是关键字，而且可以包括 Transact-SQL 标识符中通常不允许的字符。如果单引号 (') 是文字字符串的一部分，则可以用双引号 (") 表示它。

当设置为 OFF（默认）时，标识符不能用引号围住，而且必须遵循所有用于标识符的 Transact-SQL 规则。文字可以由单引号或双引号分隔。

SQL Server 还允许使用方括号 ([ ]) 分隔标识符。无论 QUOTED_IDENTIFIER 的设置如何，都可以始终使用括号标识符。有关更多信息，请参见分隔标识符。

当在计算列或索引视图上创建或操作索引时，SET QUOTED_IDENTIFIER 必须设置为 ON。

连接级设置（使用 SET 语句设置）替代 QUOTED_IDENTIFIER 的默认数据库设置。默认情况下，连接到 SQL Server 时，ODBC 和 OLE DB 客户端发出连接级 SET 语句，将 QUOTED_IDENTIFIER 设置为 ON。有关更多信息，请参见 SET QUOTED_IDENTIFIER。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsQuotedIdentifiersEnabled 属性来确定。

RECURSIVE_TRIGGERS

当设置为 ON 时，触发器可以递归激发。当设置为 OFF（默认）时，触发器不能递归激发。

说明 当 RECURSIVE_TRIGGERS 为 OFF 时，只禁止直接递归。若要禁用间接递归，还必须将 nested triggers 服务器选项设置为 0。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 IsRecursiveTriggersEnabled 属性来确定。

状态选项

状态选项控制数据库是联机还是脱机、谁可以连接到数据库以及数据库是否为只读模式。当数据库从一种状态转换到另一种状态时，终止子句可用于控制如何终止连接。

OFFLINE | ONLINE

当指定 OFFLINE 时，数据库被关闭并且干净地退出，并标记为脱机。数据库脱机时不能进行修改。

当指定 ONLINE 时，数据库处于打开状态并可供使用。ONLINE 是默认设置。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 Status 属性来确定。

READ_ONLY | READ_WRITE

指定 READ_ONLY 时，数据库处于只读模式。用户可以从数据库检索数据，但是不能修改数据。因为只读数据库不允许数据修改：

  • 系统启动时，跳过自动恢复。

  • 不能收缩数据库。

  • 只读数据库中不会发生锁定，这使查询性能更快。

指定 READ_WRITE 时，用户可以检索并修改数据。READ_WRITE 是默认设置。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 Updateability 属性来确定。

SINGLE_USER | RESTRICTED_USER | MULTI_USER

SINGLE_USER 每次允许一个用户连接到数据库。所有其它用户连接均中断。中断连接的时间段由 ALTER DATABASE 语句的终止子句控制。新的连接尝试将被拒绝。即使设置此选项的用户注销，数据库仍保持 SINGLE_USER 模式。这时，其他用户（但只能是一个）可以连接到数据库。

若要允许多个连接，必须将数据库更改为 RESTRICTED_USER 或 MULTI_USER 模式。

RESTRICTED_USER 只允许 db_owner 固定数据库角色成员以及 dbcreator 和 sysadmin 固定服务器角色成员连接到数据库，不过对连接数没有限制。在 ALTER DATABASE 语句的终止子句所指定的时间段内，不是这些角色成员的用户连接将被断开连接。而且，不合格用户所做的新连接尝试将被拒绝。

MULTI_USER 允许所有具有适当权限的用户连接到数据库。MULTI_USER 是默认设置。

此选项的状态可通过检查 DATABASEPROPERTYEX 函数的 UserAccess 属性来确定。

WITH <termination>

ALTER DATABASE 语句的终止子句指定：当数据库从一种状态转换到另一种状态时，如何终止未完成的事务。通过中断事务与数据库的连接终止事务。如果省略终止子句，则 ALTER DATABASE 语句将无限期等待，直到事务主动提交或回滚为止。

ROLLBACK AFTER integer [SECONDS]
ROLLBACK AFTER integer SECONDS 等待指定的秒数，然后中断不合格的连接。未完成的事务将回滚。当转换为 SINGLE_USER 模式时，不合格的连接是除发出 ALTER DATABASE 语句的连接以外的所有连接。当转换为 RESTRICTED_USER 模式时，不合格的连接是非 db_owner 固定数据库角色成员以及 dbcreator 和 sysadmin 固定服务器角色成员的用户连接。
ROLLBACK IMMEDIATE
ROLLBACK IMMEDIATE 立即中断不合格的连接。所有未完成的事务都将回滚。不合格的连接与 ROLLBACK AFTER integer SECONDS 所描述的连接相同。
NO_WAIT
NO_WAIT 在尝试更改数据库状态之前检查连接，如果存在某些连接，则导致 ALTER DATABASE 语句失败。当转换为 SINGLE_USER 模式时，如果存在任何其它连接，则 ALTER DATABASE 语句失败。当转换为 RESTRICTED_USER 模式时，如果存在任何不合格的连接，则 ALTER DATABASE 语句失败。

更改数据库选项

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

DATABASEPROPERTYEX

SET

sp_configure

sp_dboption

SQL Server 版本

user options 选项


创建可移动数据库

Microsoft® SQL Server™ 2000 使您得以出于只读目的创建数据库，并通过可移动介质（如 CD-ROM）分发此数据库。这对于分发包含历史数据的大型数据库（如包含上一年销售数据明细的数据库）很有用。

若要创建可移动介质数据库，则必须使用 sp_create_removable 系统存储过程，而不要使用 SQL Server 企业管理器或 CREATE DATABASE 语句创建数据库。

sp_create_removable 系统存储过程可以创建三个或更多文件：

  • 一个文件包含系统表

  • 一个文件包含事务日志

  • 一个或多个文件包含数据表

即使数据库本身有可能保留在只读媒体（如 CD-ROM）上，系统表和事务日志也会放在可写媒体上的单独文件中，以便管理任务（例如将用户添加到数据库、授予权限等）能够完成。

一个数据库可以使用多个可移动介质设备。但是，所有媒体都必须能够同时使用。例如，如果数据库使用三个光盘，则系统必须有三个 CD-ROM 驱动器，而且当使用数据库时，所有光盘都可用。

创建数据库之后，可以使用 sp_certify_removable 系统存储过程，以确保正确配置数据库以便分发在可移动介质上。如果数据库配置正确，则数据库将处于脱机状态，并允许文件复制到可移动介质上。通过使数据库处于脱机状态，可以防止用户访问数据库，而且不能修改数据库，直到数据库联机为止。要使数据库在同一台服务器上再次可用，将数据库联机即可。

在可移动介质上分发文件后，通过将这些文件附加到其它 SQL Server 实例，可使数据库变为可用。有关更多信息，请参见附加和分离数据库。

使数据库联机或脱机

Transact-SQL

SQL-DMO

请参见

sp_certify_removable

sp_create_removable


附加和分离数据库

Microsoft® SQL Server™ 2000 允许分离数据库的数据和事务日志文件，然后将其重新附加到另一台服务器，甚至同一台服务器上。分离数据库将从 SQL Server 删除数据库，但是保持在组成该数据库的数据和事务日志文件中的数据库完好无损。然后这些数据和事务日志文件可以用来将数据库附加到任何 SQL Server 实例上，包括从中分离该数据库的服务器。这使数据库的使用状态与它分离时的状态完全相同。

如果想按以下方式移动数据库，则分离和附加数据库很有用：

  • 从一台计算机移到另一台计算机，而不必重新创建数据库，然后手动还原数据库备份。

  • 移到另一物理磁盘上，例如，当包含该数据库文件的磁盘空间已用完，您希望扩充现有的文件而又不愿将新文件添加到其它磁盘上的数据库。

将数据库或数据库文件移动到另一服务器或磁盘：

  1. 分离数据库。

  4. 将数据库文件移到另一服务器或磁盘。

  5. 通过指定移动文件的新位置附加数据库。

当附加在数据库上时，必须指定主数据文件的名称和物理位置。主文件包含查找由数据库组成的其它文件所需的信息，除非自分离了数据库后那些文件中的一个或多个已改变了位置。除主文件外，还必须指出其它任何已改变位置的文件。否则，SQL Server 将试图基于存储在主文件中的不正确的文件位置信息附加文件，且不能成功附加数据库。

如果将数据库附加到的服务器不是该数据库从中分离的服务器，并且启用了分离的数据库用于复制，则应运行 sp_removedbreplication 从数据库中删除复制。或者，可以在分离数据库之前从数据库中删除复制。

分离数据库时所产生的错误可能会防碍干净地关闭数据库和重建事务日志。如果收到错误信息，请执行下列操作以纠正错误：

  1. 重新附加所有与数据库相关联的文件，而不仅是主文件。

  2. 解决导致错误信息的问题。

  3. 再次分离数据库。

附加数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

分离数据库

Transact-SQL

企业管理器

企业管理器

请参见

文件和文件组


附加单文件数据库

如果数据库只由一个数据文件和一个事务日志文件组成，则可以将数据库附加到 Microsoft® SQL Server® 2000，而不必使用事务日志文件（假定数据库干净地关闭、没有用户也没有打开的事务）。在附加数据文件之后，SQL Server 将自动创建一个新的事务日志文件。

数据库必须已经使用 sp_detach_db 系统存储过程成功地从 SQL Server 中分离。

如果想要通过电子邮件将数据库发送给其他用户，则单文件的数据库很有用。所有数据都存储在一个文件中；将这一个文件附加到 SQL Server 将自动重新创建一个事务日志，以便可以使用该数据库。

连接单文件数据库

Transact-SQL

SQL-DMO


重命名数据库

Microsoft® SQL Server™ 2000 允许更改数据库的名称。在重命名数据库之前，应该确保没有人使用该数据库，而且数据库设置为单用户模式。只要遵循用于标识符的规则，则数据库名称可以包含任意字符。

重命名数据库

Transact-SQL

SQL-DMO

请参见

使用标识符


更改数据库所有者

Microsoft® SQL Server™ 2000 允许更改当前数据库的所有者。任何具有连接到 SQL Server 权限的用户（SQL Server 登录或 Microsoft Windows NT® 用户）都可成为数据库的所有者。

不能更改系统数据库（master、model 和 tempdb）的所有权。

更改数据库的所有者

Transact-SQL

SQL-DMO


更改数据库状态的事务终止

Microsoft® SQL Server™ 2000 能够很容易地关闭数据库或更改数据库的状态、自动终止受影响的用户会话并回滚相关的事务。受影响的会话可以立即终止，也可以在可选的超时限制下继续运行直到正常终止。

经常需要停止数据库上的活动，或将数据库上的活动限制为执行维护操作或其它不会影响服务器运行的操作，例如：

  • 单用户模式：只允许一个用户

  • 受限用户模式：只允许 db_owner、dbcreator 或 sysadmin 角色的成员

  • 脱机：数据库脱机

  • 只读模式：不允许更改

转换到这些状态中的任何一种都要求终止不满足新状态要求的事务和相关会话。

有三种类型的事务终止：

  • 正态分布

    防止启动新的事务。允许未完成的事务主动提交或回滚。

  • 带超时限制的正常事务终止

    防止启动新的事务。允许未完成的事务主动提交或回滚，直到到达超时限制，这时将回滚事务。

  • 即时事务终止

    即时终止防止启动新的事务，并且无条件地回滚未完成的事务。

开始进行更改的用户保持连接状态并可以执行进一步的命令。

使用 ALTER DATABASE 语句指定数据库的状态和事务终止类型。

请参见

ALTER DATABASE


查看数据库

在进行疑难解答或考虑更改数据库时，可以查看数据库的定义及其配置设置。

查看数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看数据库的设置

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看服务器上的数据库列表

Transact-SQL

企业管理器


显示数据库和事务日志空间

Microsoft® SQL Server™ 2000 可以显示数据库中的行数、保留的磁盘空间和数据库中的表所使用的磁盘空间。SQL Server 还能显示整个数据库使用的保留磁盘空间，以及关于数据库中事务日志空间使用情况的统计信息。表明在您的数据库中有多少数据、数据库是否需要扩充（如果不允许自动增长），以及数据库的增长速度（如果保留所使用的数据空间的历史记录）。

显示数据库的数据空间信息

Transact-SQL

企业管理器

企业管理器

SQL-DMO

显示数据库的日志空间信息

Transact-SQL

企业管理器

企业管理器

SQL-DMO


编写数据库文档和脚本

使用 Microsoft® SQL Server™ 2000，可以通过生成一个或多个 SQL 脚本，编写现有的数据库结构（架构）文档。可使用 SQL Server 企业管理器、SQL 查询分析器或任何文本编辑器查看 SQL 脚本。

作为 SQL 脚本生成的架构可以有许多用途，包括：

  • 维护备份脚本，该脚本将允许用户重新创建所有用户、组、登录和权限。

  • 创建和更新数据库开发代码。

  • 从现有的架构创建一个测试或开发环境。

  • 培训新雇员。

SQL 脚本包含用于创建数据库及其对象的语句的描述。可以从现有数据库中的对象生成脚本，然后通过运行该数据库的脚本将这些对象添加到其它数据库。实际上，这样做重新创建了整个数据库结构，以及所有的单个数据库对象。可以生成下列对象的架构，并保存为脚本：

表 | 用户定义的数据类型
索引 | 触发器
视图 | 用户、组和角色
存储过程 | 登录
默认值 | 规则
表键/声明引用完整性 (DRI) | 对象级权限
全文索引 |

所生成的对象架构可以保存在一个 SQL 脚本文件中，或者保存在多个文件中，其中每个文件都只包含一个对象的架构。还可以将所生成的单个对象（或一组对象）的架构保存到一个或多个 SQL 脚本文件中。可以生成的 SQL 脚本文件示例包括：

  • 保存到单个 SQL 脚本文件中的整个数据库。

  • 保存到一个或多个 SQL 脚本文件中的数据库中的一个、几个或所有表的只有表的架构。

  • 保存到一个 SQL 脚本文件表和索引架构，保存到其它 SQL 脚本文件中的存储过程，以及保存到又一个 SQL 脚本文件中的默认设置和规则。

生成脚本

企业管理器

企业管理器

SQL-DMO

请参见

ALTER DATABASE


数据库维护计划向导

数据库维护计划向导可以用来帮助您设置核心维护任务，这些任务对于确保数据库的良好执行、定期备份以防系统失败，以及检查一致性方面很有必要。数据库维护计划向导将创建 Microsoft® SQL Server™ 2000 作业，该作业自动按所计划的间隔执行这些维护任务。

可以进行调度使之自动运行的维护任务包括：

  • 通过用新填充因子重建索引，以重新组织数据和索引页上的数据。这就确保了数据库页包含均匀分发的数据量和可用空间，从而允许以后更快地增长。有关更多信息，请参见填充因子。

  • 通过删除空数据库页压缩数据文件。

  • 更新索引统计，以确保查询优化器有关于表中数据值分发的最新信息。这使得查询优化器能够更好地判断访问数据的最佳方法，因为它有关于存储在数据库中的数据的更多信息。虽然 SQL Server 自动定期更新索引统计，但是此选项可以强迫统计立即更新。

  • 对数据库内的数据和数据页执行内部一致性检查，以确保系统或软件问题没有损坏数据。

  • 备份数据库和事务日志文件。数据库和日志备份可以保留一段指定时间。这使您可以创建备份的历史记录，如果需要将数据库还原到最近一次备份之前的时间，就可以使用这些备份。

  • 设置日志传送。日志传送允许将事务日志从一个数据库（源）不断地传送到另一个数据库（目的）。通过使目的数据库与源数据库保持同步，使您得以具有备用服务器，并提供将查询处理工作从主计算机（源服务器）卸载到只读目的服务器的方法。

维护任务产生的结果可以作为报告写到文本文件、HTML 文件，或 msdb 数据库的 sysdbmaintplan_history 表中。报告也可以通过电子邮件发送给操作员。

启动数据库维护计划向导

企业管理器

企业管理器

配置日志传送

企业管理器


删除数据库

当不再需要数据库，或如果它被移到另一数据库或服务器时，即可删除该数据库。数据库删除之后，文件及其数据都从服务器上的磁盘中删除。一旦删除数据库，它即被永久删除，并且不能进行检索，除非使用以前的备份。不能分离系统数据库 msdb、master、model 和 tempdb。

建议您在数据库删除之后备份 master 数据库，因为删除数据库将更新 master 中的系统表。如果 master 需要还原，则从上次备份 master 之后删除的所有数据库都将仍然在系统表中有引用，因而可能导致出现错误信息。

删除数据库

Transact-SQL

企业管理器

企业管理器

SQL-DMO

创建数据库备份

Transact-SQL

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

备份和还原数据库


表

表是包含数据库中所有数据的数据库对象。表定义为列的集合。与电子表格相似，数据在表中是按行和列的格式组织排列的。每行代表唯一的一条记录，而每列代表记录中的一个域。例如，在包含公司雇员数据的表中每一行代表一名雇员，各列分别表示雇员的详细资料，如雇员编号、姓名、地址、职位以及家庭电话号码等。


设计表

设计数据库时，应先确定需要什么样的表，各表中都有哪些数据以及各个表的存取权限等等。在创建和操作表的过程中，将对表进行更为细致的设计。

创建一个表最有效的方法是将表中所需的信息一次定义完成，包括数据约束和附加成分。也可以先创建一个基础表，向其中添加一些数据并使用一段时间。这种方法使您可以在添加各种约束、索引、默认设置、规则和其它对象形成最终设计之前，发现哪些事务最常用，哪些数据经常输入。

最好在创建表及其对象时预先将设计写在纸上。设计时应注意：

  • 表所包含的数据的类型。

  • 表的各列及每一列的数据类型（如果必要，还应注意列宽）。

  • 哪些列允许空值。

  • 是否要使用以及何时使用约束、默认设置或规则。

  • 所需索引的类型，哪里需要索引，哪些列是主键，哪些是外键。

请参见

索引


指定列数据类型

设计表时首先要为每列指派数据类型。数据类型定义了各列所允许的数据值。若要为列指派数据类型，请使用 Microsoft® SQL Server™ 2000 的基本数据类型，或基于这些系统数据类型创建用户定义的数据类型。例如，如果列中只含有姓名，可以将该列定义为字符型。以此类推，如果列中只含有数值，就可以将该列定义为数字数据类型。有关用户定义数据类型的更多信息，请参见创建用户定义的数据类型。

SQL Server 也针对几种基本数据类型支持使用 SQL-92 同义词。有关更多信息，请参见数据类型同义词。

强制数据完整性

系统和用户定义数据类型可以用来强制实现数据的完整性，因为所输入的或所更改的数据都必须与原 CREATE TABLE 语句中所指定的类型一致。例如，不能在定义为 datetime 的列中存储姓，因为 datetime 列只能接受有效日期。一般地，在数字列中存储数字数据，尤其在以后要对它进行运算时更应如此。


二进制数据

二进制数据由十六进制数表示。例如，十进制数 245 等于十六进制数 F5。在 Microsoft® SQL Server™ 2000 中，二进制数据使用 binary、varbinary 和 image 数据类型存储。指派为 binary 数据类型的列在每行中都是固定的长度（最多为 8 KB）。指派为 varbinary 数据类型的列，各项所包含的十六进制数字的个数可以不同（最多为 8 KB）。image 数据列可以用来存储超过 8 KB 的可变长度的二进制数据，如 Microsoft Word 文档、Microsoft Excel 电子表格、包含位图的图像、图形交换格式 (GIF) 文件和联合图像专家组 (JPEG) 文件。

除非数据长度超过 8KB 时用 image 数据类型存储，一般宜用 varbinary 类型来存储二进制数据。建议列宽的定义不超过所存储的二进制数据可能的最大长度。

请参见

binary 和 varbinary

image

使用二进制数据

使用数据类型

使用 text 和 image 数据


字符数据

字符数据由字母、符号和数字组成。例如，有效字符数据包括"928"、"Johnson"和"(0*&(%B99nh jkJ"。在 Microsoft® SQL Server™ 2000 中，字符数据使用 char、varchar 和 text 数据类型存储。当列中各项的字符长度数时可用 varchar 类型，但任何项的长度都不能超过 8 KB。当列中各项为同一固定长度时使用 char类型（最多 8KB）。text 数据类型的列可用于存储大于 8KB 的 ASCII 字符。例如，由于 HTML 文档均由 ASCII 字符组成且一般长于 8KB，所以用浏览器查看之前应在 SQL Server 中存储在 text 列中。

建议字符列宽度的定义不超过所存储的字符数据可能的最大长度。

若要在 SQL Server中存储国际化字符数据，请使用 nchar、nvarchar 和 ntext 数据类型。

请参见

char 和 varchar

ntext、text 和 image

使用 char 和 varchar 数据

使用数据类型

使用 text 和 image 数据

使用 Unicode 数据


Unicode 数据

在 Microsoft® SQL Server™ 2000 中，传统上非 Unicode 数据类型允许使用由特定字符集定义的字符。字符集是在安装 SQL Server 时选择的，不能更改。使用 Unicode 数据类型，列可存储由 Unicode 标准定义的任何字符，包含由不同字符集定义的所有字符。Unicode 数据类型需要相当于非 Unicode 数据类型两倍的存储空间。

Unicode 数据使用 SQL Server 中的 nchar、varchar 和 ntext 数据类型进行存储。对于存储来源于多种字符集的字符的列，可采用这些数据类型。当列中各项所包含的 Unicode 字符数不同时（至多为 4000），使用 nvarchar 类型。当列中各项为同一固定长度时（至多为 4000 个 Unicode 字符），使用 nchar 类型。当列中任意项超过 4000 个 Unicode字符时，使用 ntext 类型。

说明 SQL Server 的 Unicode 数据类型是基于 SQL-92 标准中的国家字符数据类型。SQL-92 使用前缀字符 n 标识这些数据类型及其值。

请参见

排序规则

nchar 和 nvarchar

ntext、text 和 image

使用 Unicode 数据


日期和时间数据

日期和时间数据由有效的日期或时间组成。例如，有效日期和时间数据既包括"4/01/98 12:15:00:00:00 PM"，也包括"1:28:29:15:01 AM 8/17/98"。在 Microsoft® SQL Server™ 2000 中，日期和时间数据使用 datetime 和 smalldatetime 数据类型存储。使用 datetime 数据类型存储从 1753 年 1 月 1 日至 9999 年 12 月 31 日的日期（每个数值要求 8 个字节的存储空间）。使用 smalldatetime 数据类型存储从 1900 年 1 月 1 日至 2079 年 6 月 6 日的日期（每个数值要求 4 个字节的存储空间）。

请参见

datetime 和 smalldatetime

使用数据类型

使用日期和时间数据


数字数据

数字数据只包含数字。数字数据包括正数、负数、小数、分数和整数。

整型数据

整型数据由负整数或正整数组成，如 -15、0、5 和 2509。在 Microsoft® SQL Server™ 2000 中，整型数据使用 bigint、int、smallint 和 tinyint 数据类型存储。bigint 数据类型可存储的数字范围比 int 数据类型广。int 数据类型比 smallint 数据类型的存储范围大，而 smallint 的数值范围又比 tinyint 类型大。

使用 bigint 数据类型存储从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 范围内的数字。存储大小为 8 个字节。

int 数据类型的存储范围是 -2,147,483,648 至 2,147,483,647（每个值需 4 个字节的存储空间）。

smallint 数据类型的存储范围只有 -32,768 至 32,767（每个值需 2 个字节的存储空间），tinyint 数据类型只能存储 0 至 255 范围内的数字（每个值需 1 个字节的存储空间）。

小数数据

Decimal 数据包含存储在最小有效数上的数据。在 SQL Server中，小数数据使用 decimal 或 numeric 数据类型存储。存储 decimal 或 numeric 数值所需的字节数取决于该数据的数字总数和小数点右边的小数位数。例如，存储数值 19283.29383 比存储 1.1 需要更多的字节。

在 SQL Server中，numeric 数据类型等价于 decimal 数据类型。

近似数字数据

近似数字（浮点）数据包括按二进制计数系统所能提供的最大精度保留的数据。在 SQL Server 中，近似数字数据以 float 和 real 数据类型存储。例如，分数 1/3 表示成小数形式为 0.333333（循环小数），该数字不能以近似小数数据精确表示。因此，从 SQL Server 获取的值可能并不准确代表存储在列中的原始数据。又如，以 .3, .6, .7 结尾的浮点数均为数字近似值。

请参见

decimal 和 numeric

float 和 real

int、bigint、smallint 和 tinyint

使用数据类型

使用 decimal、float 和 real 数据

使用整型数据


货币数据

货币数据表示正的或负的货币值。在 Microsoft® SQL Server™ 2000 中使用 money 和 smallmoney 数据类型存储货币数据。货币数据存储的精确度为四位小数。可以存储在 money 数据类型中的值的范围是 -922,337,203,685,477.5808 至 +922,337,203,685,477.5807（需 8 个字节的存储空间）。可以存储在 smallmoney 数据类型中的值的范围是 -214,748.3648 至 214,748.3647（需 4 个字节的存储空间）。如果数值超过了上述范围，则可使用 decimal 数据类型代替。

请参见

money 和 smallmoney

使用数据类型

使用货币数据


特殊数据

特殊数据包括不能用前面所述的二进制、字符、Unicode、日期和时间、数字和货币数据类型表示的数据。

Microsoft® SQL Server™ 2000 包含四种特殊数据类型：

  • timestamp

    用于表示 SQL Server 在一行上的活动顺序，按二进制格式以递增的数字来表示。当表中的行发生变动时，用从 @@DBTS 函数获得的当前数据库的时间戮值来更新时间戮。timestamp 数据与插入或修改数据的日期和时间无关。若要自动记录表中数据更改的时间，使用 datetime 或 smalldatetime 数据类型记录事件或触发器。

    说明 在 SQL Serve 中 rowversion 是 timestamp 的同义词。

  • bit

    bit 数据类型只能包括 0 或 1。可以用 bit 数据类型代表 TRUE 或 FALSE、YES 或 NO。例如，询问客户是否为初次访问的问题可存储在 bit 列中。

  • uniqueidentifier

    以一个 16 位的十六进制数表示全局唯一标识符 (GUID)。当需要在多行中唯一标识某一行时可使用 GUID。例如，可使用 unique_ identifier 数据类型定义一个客户标识代码列，以编辑公司来自多个国家/地区的总的客户名录。

  • sql_variant

    一种存储 SQL Server 所支持的各种数据类型（text、ntext、timestamp 和 sql_variant 除外）值的数据类型。

  • table

    一种特殊的数据类型，存储供以后处理的结果集。table 数据类型只能用于定义 table 类型的局部变量或用户定义函数的返回值。

  • 用户定义

    允许使用用户定义数据类型，例如，product_code 可设计为基于 char 数据类型的两个大写字母后跟 5 位供应商号码的格式。

请参见

bit

timestamp

uniqueidentifier

使用数据类型

使用特殊数据

使用 uniqueidentifier 数据


创建用户定义的数据类型

用户定义数据类型基于 Microsoft® SQL Server™ 2000 中的系统数据类型。当多个表的列中要存储同样类型的数据，且想确保这些列具有完全相同的数据类型、长度和为空性时，可使用用户定义数据类型。例如，可以基于 char 数据类型创建名为 postal_code 的用户定义数据类型。

创建用户定义的数据类型时必须提供以下三个参数：

  • 名称

  • 新数据类型所依据的系统数据类型

  • 为空性（数据类型是否允许空值）

    如果为空性未明确定义，系统将依据数据库或连接的 ANSI Null 默认设置进行指派。

说明 如果用户定义数据类型是在 model 数据库中创建的，它将作用于所有用户定义的新数据库中。如果数据类型在用户定义的数据库中创建，则该数据类型只作用于此用户定义的数据库。

创建用户定义的数据类型

Transact-SQL

企业管理器

企业管理器

SQL-DMO

删除用户定义数据类型

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

ALTER TABLE

CREATE TABLE

使用数据类型


Text in Row 数据

Microsoft® SQL Server™ 2000 支持在数据行中存储小型到中等的 text、ntext 和 image 值。该功能最适于这样的表：text、ntext 和 image 列中的数据通常读取或写入一个单元，并且大多数引用该表的语句都使用 text、ntext 和 image 数据。

除非指定 text in row 选项，否则 text、ntext 或 image 字符串为存储在数据行外的大型（最大 2GB）字符串或二进制字符串。数据行只包括一个 16 字节的文本指针，该指针指向一个树的根节点，该树由映射存储串片段的页的内部指针构成。有关 text、ntext 或 image 字符串存储的更多信息，请参见 text、ntext 和 image 数据。

可为包含 text、ntext 或 image 列的表设置 text in row 选项。还可以指定 text in row 选项限制，从 24 到 7,000 字节。如果设置了该选项，则在下列情况下，text、ntext 或 image 字符串直接存储在数据行中：

  • 字符串的长度比指定的长度限制短。

  • 数据行中有足够的可用空间容纳字符串。

当 text、ntext 或 image 串被存储在数据行时，SQL Server 就无须访问单独的页或页的集合来读写这些串。这使得读写 text、ntext 或 image 这些行中的串就象读写 varchar、 nvarchar 或 varbinary 串一样快。

如果 text、ntext 或 image 字符串的长度比 text in row 选项的限制或行中的可用空间长，则本应存储在指针树根节点中的指针集将存储在行中。下列情况下，将指针存储在行中：

  • 存储指针所需的空间量比 text in row 选项限制指定的少。

  • 数据行中有足够的可用空间容纳指针。

当指针从根节点移至行本身时，SQL Server 无须使用根节点。这可在读取或写入字符串时消除页访问，从而加快处理速度。

当使用根节点时，它们被存储为 text、ntext 或 image 页中的一个字符串片段，并且可最多容纳五个内部指针。SQL Server 的行需要 72 字节的空间存储行内字符串的五个指针。如果当 text in row 选项打开时行中没有足够的空间容纳指针，SQL Server 必须分配一个 8-K 页容纳它们。不应将 text in row 限制设置为小于 72 字节，除非确定存储在列中的所有字符串都较短或超过 3 MB。

当 text、ntext 或 image 字符串存储在行中时，它们的存储类似于长度可变的字符串。例如，如果 text in row 选项的限制为 500 字节，则在行中存储一个 200 字节的字符串时，SQL Server 将只使用存储该字符串所需的字节数。如果插入的字符串比 500 字节长，因而指针存储在行中，则 SQL Server 只使用足够容纳指针的空间，而不是全部 500 字节。

如果某个表有多个 text、ntext 或 image 列，则当尝试插入多个 text、ntext 或 image 字符串时，SQL Server 将按基于列 ID 的顺序给这些字符串逐个分配空间。例如，假设有一个包含四个 text 列的表，并且将 text in row 选项的限制设置为 1000。然后插入某行，而该行中每个 text 列都为 900 字节的字符串，并且有足够的数据用于表中的所有其它列，因此在行中只有 3,000 字节的可用空间容纳 text 字符串。前三个 text 列的字符串存储在行中，使用 3,000 可用字节中的 2,700 字节。第四个 text 列的字符串不存储在行中，但根节点的指针存储在行中。

启用和禁用 text in row 选项

可使用 sp_tableoption 为表启用 text in row 选项：

sp_tableoption N'MyTable', 'text in row', 'ON'

或者，可以为可在数据行中存储的 text、ntext 和 image 字符串长度指定从 24 到 7.000 字节的最大限制：

sp_tableoption N'MyTable', 'text in row', '1000'

如果指定的是 on 而不是一个特定的限制，则此限制的默认值为 256 字节。该默认值使您得以从 text in row 选项中获得最多的性能收益。尽管在通常情况下，不应将此值设为低于 72，但也不应把此值设得过高，特别是对那些大多数语句都不引用 text、ntext 和 image 列的表或者具有多个 text、ntext 和 image 列的表。如果设置了较大的 text in row 限制，且行本身存储了许多字符串，则可以显著减少存储在每页上的数据行数。如果大多数引用表的语句不访问 text、ntext 或 image 列，则减少页中的行数会增加处理查询时必须读取的页数。减少每页上的行数会增加索引及页的大小，优化器找不到可用的索引时可能需要扫描这些索引和页。text in row 限制的默认值 256 足以确保小型字符串和根文本指针存储在行中，同时不会使每页上的行减少得太多以至影响性能。

也可以使用 sp_tableoption 关闭此选项，这可以通过指定 off 或 0 选项值来实现：

sp_tableoption N'MyTable', 'text in row', 'OFF'
text in row 选项的效果

text in row 选项具有下列效果：

  • 打开 text in row 选项后，不能使用 READTEXT、UPDATETEXT 或 WRITETEXT 语句读取或修改存储在表中的任何 text、ntext 或 image 值的任何部分。在 SELECT 语句中可以读取整个 text、ntext 或 image 字符串，或者使用 SUBSTRING 函数读取部分字符串。所有引用表的 INSERT 或 UPDATE 语句必须指定完整的字符串，并且不能只修改 text、ntext 或 image 字符串的一部分。

  • 当第一次启用 text in row 选项时，现有 text、ntext 或 image 字符串不会立即转换成行内字符串。仅当随后更新字符串时，这些字符串才转换成行内字符串。在打开 text in row 选项后插入的任何 text、ntext 或 image 字符串将作为行内字符串插入。

  • 关闭 text in row 选项可能是长时间运行的日志记录操作。表被锁定且所有行内 text、ntext 和 image 字符串都转换成常规 text、ntext 和 image 字符串。运行命令所需的时间和需要修改的数据量，取决于必须从行内字符串转换成常规字符串的 text、ntext 和 image 字符串的数量。

  • text in row 选项不影响用于 SQL Server 的 OLE DB 提供程序或 SQL Server ODBC 驱动程序的操作，而只会加快访问 text、ntext 和 image 数据的速度。

  • 打开 text in row 选项后，DB-Library text 和 image 函数（如 dbreadtext 和 dbwritetext）不能在表上使用。

下列各项的 text in row 选项自动设置成 256：

  • 含有表数据类型的变量。

  • 由返回 table 的用户定义函数所返回的表。

不能更改该设置。


自动编号和标识符列

对任何表都可创建包含系统所生成序号值的一个标识符列，该序号值唯一标识表中的一行。例如，当在表中插入行时，标识符列可自动为应用程序产生唯一的客户收据号码。标识符列在其所定义的表中包含的数值通常是唯一的。这意味着在包含标识符列的其它表中可使用与之相同的数值进行标识。但是，由于标识符值通常是在一个独立表的上下文中使用而不与其它表中的标识符列发生关联，所以通常不会发生问题。

可在每个表上创建一个全局唯一标识符列，而该列中包含对世界上所有网络计算机均不重复的值。当必须对来自多个数据库系统的相似数据进行合并时（例如，在包含位于世界各地分公司的数据的客户帐单系统中），包含全局唯一值的列很有用。当数据汇集到中心进行合并和制作报表时，使用全局唯一值可防止不同国家/地区的客户拥有相同的帐单号或客户 ID。

Microsoft® SQL Server™ 2000 在合并复制中使用全局唯一标识符列，以确保在表的多个复本间唯一标识行。

请参见

创建和修改标识符列

合并复制

NEWID

uniqueidentifier

使用 Uniqueidentifier 数据


使用约束、默认值和空值

设计表时需要识别列的有效值并决定如何强制实现列中数据的完整性。Microsoft® SQL Server™ 2000 提供多种强制列中数据完整性的机制：

  • PRIMARY KEY 约束

  • FOREIGN KEY 约束

  • UNIQUE 约束

  • CHECK 约束

  • DEFAULT 定义

  • 为空性

请参见

数据完整性


PRIMARY KEY 约束

表中经常有一个列或列的组合，其值能唯一地标识表中的每一行。这样的一列或多列称为表的主键，通过它可强制表的实体完整性。当创建或更改表时可通过定义 PRIMARY KEY 约束来创建主键。

一个表只能有一个 PRIMARY KEY 约束，而且 PRIMARY KEY 约束中的列不能接受空值。由于 PRIMARY KEY 约束确保唯一数据，所以经常用来定义标识列。

当为表指定 PRIMARY KEY 约束时，Microsoft® SQL Server™ 2000 通过为主键列创建唯一索引强制数据的唯一性。当在查询中使用主键时，该索引还可用来对数据进行快速访问。

如果 PRIMARY KEY 约束定义在不止一列上，则一列中的值可以重复，但 PRIMARY KEY 约束定义中的所有列的组合的值必须唯一。

如下图所示，titleauthor 表中的 au_id 和 title_id 列组成该表的组合 PRIMARY KEY 约束，以确保 au_id 和 title_id 的组合唯一。

当进行联接时，PRIMARY KEY 约束将一个表与另一个表相联。例如，若要确定作者与书名的对应关系，可以使用 authors 表、titles 表和 titleauthor 表的三向联接。因为 titleauthor 包含 au_id 和 title_id 两列，对 titles 表的访问可由 titleauthor 和 titles 之间的关联进行。

请参见

创建和修改 PRIMARY KEY 约束


FOREIGN KEY 约束

外键 (FK) 是用于建立和加强两个表数据之间的链接的一列或多列。通过将保存表中主键值的一列或多列添加到另一个表中，可创建两个表之间的链接。这个列就成为第二个表的外键。

当创建或更改表时可通过定义 FOREIGN KEY 约束来创建外键。

例如，数据库 pubs 中的 titles 表与 publishers 表有链接，因为在书名和出版商之间存在逻辑联系。titles 表中的 pub_id 列与 publishers 表中的主键列相对应。titles 表中的 pub_id 列是到 publishers 表的外键。

FOREIGN KEY 约束并不仅仅只可以与另一表的 PRIMARY KEY 约束相链接，它还可以定义为引用另一表的 UNIQUE 约束。FOREIGN KEY 约束不允许空值，但是，如果任何组合 FOREIGN KEY 约束的列包含空值，则将跳过 FOREIGN KEY 约束的校验。

说明 FOREIGN KEY 约束可引用同一数据库中的表或同一表（自引用表）内的列，例如，一个包含下面三列的雇员表：employee_number、employee_name 和 manager_ employee_number。由于经理本身也是雇员，所以从 manager_employee_number 列到 employee_number 列存在外键关系。

尽管 FOREIGN KEY 约束的主要目的是控制存储在外键表中的数据，但它还可以控制对主键表中数据的修改。例如，如果在 publishers 表中删除一个出版商，而这个出版商的 ID 在 titles 表中记录书的信息时使用了，则这两个表之间关联的完整性将被破坏，titles 表中该出版商的书籍因为与 publishers 表中的数据没有链接而变得孤立了。FOREIGN KEY 约束防止这种情况的发生。如果主键表中数据的更改使之与外键表中数据的链接失效，则这种更改是不能实现的，从而确保了引用完整性。如果试图删除主键表中的行或更改主键值，而该主键值与另一个表的 FOREIGN KEY 约束值相关，则该操作不可实现。若要成功更改或删除 FOREIGN KEY 约束的行，可以先在外键表中删除外键数据或更改外键数据，然后将外键链接到不同的主键数据上去。

FOREIGN KEY 约束是索引的候选约束，其原因有以下两点：

  • 对 PRIMARY KEY 约束的更改可由相关表中的 FOREIGN KEY 约束校验。

  • 当在查询中组合来自相关表中的数据时，经常在联接条件中使用外键列，方法是将一个表的 FOREIGN KEY 约束中的列与另一个表中的主键列或唯一键列匹配。索引使 Microsoft® SQL Server™ 2000 得以快速查找外键表中的相关数据。但是，创建索引不是必需的。即使没有在表间定义 PRIMARY KEY 或 FOREIGN KEY 约束，也可以对来自两个相关表中的数据进行组合，但两个表间的外键关系说明已用其键作为条件对其进行了优化，以便组合到查询中。有关在联接中使用 FOREIGN KEY 约束的更多信息，请参见联接基础知识。

请参见

创建和修改 FOREIGN KEY 约束

索引


级联引用完整性约束

当用户试图删除或更新外键所指向的键时，级联引用完整性约束使您得以定义 Microsoft® SQL Server™ 2000 所采取的操作。

CREATE TABLE 和 ALTER TABLE 语句的 REFERENCES 子句支持 ON DELETE 和 ON UPDATE 子句：

  • [ ON DELETE { CASCADE | NO ACTION } ]

  • [ ON UPDATE { CASCADE | NO ACTION } ]

如果没有指定 ON DELETE 或 ON UPDATE，则默认为 NO ACTION。NO ACTION 指定的行为与 SQL Server 的早期版本中发生的行为相同。

ON DELETE NO ACTION

指定如果试图删除某行，而该行含有由其它表的现有行中的外键所引用的键，则产生错误并回滚 DELETE。

ON UPDATE NO ACTION

指定如果试图更新某行中的键值，而该行含有由其它表的现有行中的外键所引用的键，则产生错误并回滚 UPDATE。

CASCADE 允许在表间级联键值的删除或更新操作，这些表的外键关系可追溯到执行修改的表。不能为任何具有 timestamp 列的外键和主键指定 CASCADE。

ON DELETE CASCADE

指定如果试图删除某行，而该行含有由其它表的现有行中的外键所引用的键，则也将删除所有包含那些外键的行。如果在目标表上也定义了级联引用操作，则对从那些表中删除的行同样采取指定的级联操作。

ON UPDATE CASCADE

指定如果试图更新某行中的键值，而该行的键值由其它表的现有行中的外键所引用，则所有外键值也将更新成为该键指定的新值。如果在目标表上也定义了级联引用操作，则对在那些表中更新的键值同样采取指定的级联操作。

级联引用操作的示例可以基于 Northwind 中 Products 表上的 FK_Products_Suppliers 约束。该约束建立了从 Products 表中的 SupplierID 列到 Suppliers 表中的 SupplierID 主键列的外键关系。如果为约束指定了 ON DELETE CASCADE，则删除 SupplierID = 1 的 Suppliers 表中的行时也将删除 SupplierID = 1 的 Products 表中的三行。如果为该约束指定了 ON UPDATE CASCADE，则更新 Suppliers 表中从 1 到 55 的 SupplierID 值也将更新 Products 表中 SupplierID 值当前等于 1 的三行的 SupplierID 值。

不能为具有 INSTEAD OF 触发器的表指定级联操作。为表定义级联操作后，就不能向该表添加 INSTEAD OF 触发器。

多个级联操作

单独的 DELETE 或 UPDATE 语句可启动一系列级联引用操作。例如，假设有一个数据库包含三个表：TableA、TableB 和 TableC。针对 TableA 中的主键，用 ON DELETE CASCADE 定义 TableB 中的外键。针对 TableB 中的主键，用 ON DELETE CASCADE 定义 TableC 中的外键。如果 DELETE 语句删除 TableA 中的行，则该操作也将删除 TableB 中具有与 TableA 中所删除的主键匹配的任何外键中的所有行，然后删除 TableC 中具有与 TableB 中所删除的主键匹配的任何外键中的所有行。

由单个 DELETE 或 UPDATE 触发的一系列级联引用操作必须构成不包含循环引用的树。在 DELETE 或 UPDATE 所产生的所有级联引用操作的列表中，每个表只能出现一次。级联引用操作树到任何给定表的路径必须只有一个。树的任何分支在遇到指定了 NO ACTION 或默认为 NO ACTION 的表时终止。

触发器和级联引用操作

级联引用操作按下列顺序激发 AFTER 触发器：

  1. 首先执行由原始 DELETE 或 UPDATE 直接导致的所有级联引用操作。

  2. 当完成原始级联引用操作后，无论是否更新了任何行，都将激发原始表上的 AFTER 触发器。

  3. 然后激发级联引用操作链中表上的 AFTER 触发器，但只有已更新或删除了表中的一行或多行时才激发。

如果任何原始级联引用操作集生成任何错误，则产生错误，不激发 AFTER 触发器，并且回滚 DELETE 或 UPDATE。

AFTER 触发器可执行 DELETE 或 UPDATE 语句以启动其它级联引用操作链。每个辅助的引用操作链分开处理。这些辅助的引用操作链的行为与主链一样。在激发任何辅助触发器之前，完成所有的辅助引用操作。在每个独立的单元中，不定义执行级联引用操作和激发受影响的触发器的顺序。

具有 INSTEAD OF 触发器的表不能同时具有指定级联操作的 REFERENCES 子句。但是，在作为级联操作目标的表上，AFTER 触发器可在其它表或视图上执行 INSERT、UPDATE 或 DELETE 语句，以激发在该表或视图上定义的 INSTEAD OF 触发器。

级联引用约束目录信息

下列目录信息是关于级联引用约束的。

Transact-SQL OBJECTPROPERTY 函数支持 property 参数的下面两个新值。

值 | 对象 | 描述
CnstIsDeleteCascade | 约束 | 用 ON DELETE CASCADE 定义的 FOREIGN KEY 约束
CnstIsUpdateCascade | 约束 | 用 ON UPDATE CASCADE 定义的 FOREIGN KEY 约束

当指定 ON UPDATE CASCADE 或 ON DELETE CASCADE 时，REFERENTIAL_CONSTRAINTS 信息架构视图返回 UDPATE_RULE 或 DELETE_RULE 列中的 CASCADE。当指定 ON UPDATE NO ACTION 或 ON DELETE NO ACTION，或者根本没有指定 ON UPDATE 或 ON DELETE 时，返回 NO ACTION。

当指定 CASCADE 时，sp_fkeys 和 sp_foreignkeys 所返回的 UPDATE_RULE 和 DELETE_RULE 列被设置成 1，当指定或默认为 NO ACTION 时则返回 0。

当将外键指定为 sp_help 的对象时，输出结果集包含下面两个新列。

列名 | 数据类型 | 描述
delete_action | nvarchar(9) | 表示删除操作是 CASCADE、NO ACTION 还是暂缺（不可用）。
update_action | nvarchar(9) | 表示更新操作是 CASCADE、NO ACTION 还是暂缺（不可用）。


UNIQUE 约束

可使用 UNIQUE 约束确保在非主键列中不输入重复值。尽管 UNIQUE 约束和 PRIMARY KEY约束都强制唯一性，但在强制下面的唯一性时应使用 UNIQUE 约束而不是 PRIMARY KEY 约束：

  • 非主键的一列或列组合。

    一个表可以定义多个 UNIQUE 约束，而只能定义一个 PRIMARY KEY 约束。

  • 允许空值的列。

    允许空值的列上可以定义 UNIQUE 约束，而不能定义 PRIMARY KEY 约束。

FOREIGN KEY 约束也可引用 UNIQUE 约束。

请参见

创建和修改 UNIQUE 约束


CHECK 约束

CHECK 约束通过限制输入到列中的值来强制域的完整性。这与 FOREIGN KEY 约束控制列中数值相似。区别在于它们如何判断哪些值有效：FOREIGN KEY 约束从另一个表中获得有效数值列表，CHECK 约束从逻辑表达式判断而非基于其它列的数据。例如，通过创建 CHECK 约束可将 salary 列的取值范围限制在 $15,000 至 $100,000 之间，从而防止输入的薪金值超出正常的薪金范围。

可以通过任何基于逻辑运算符返回结果 TRUE 或 FALSE 的逻辑（布尔）表达式来创建 CHECK 约束。对上例，逻辑表达式为：

salary >= 15000 AND salary <= 100000

对单独一列可使用多个 CHECK 约束。按约束创建的顺序对其取值。通过在表一级上创建 CHECK 约束，可以将该约束应用到多列上。例如，多列 CHECK 约束可以用来判明 country 列值为 USA 且 state 列值为两个字母值所对应的行。这样就允许在一处同时检查多个条件。

请参见

创建和修改 CHECK 约束


DEFAULT 定义

记录中的每一列均必须有值，即使它是 NULL。可能会有这种情况，当向表中装载新行时可能不知道某一列的值，或该值尚不存在。如果该列允许空值，就可以将该行赋予空值。由于有时不希望有可为空的列，因此如果合适，更好的解决办法可能是为该列定义 DEFAULT 定义。例如，通常将数字型列的默认值指定为零，将字符串列的默认值指定为暂缺。

当将某行装载到为行定义了 DEFAULT 的表中时，若没有指定列值，则是隐性要求 Microsoft® SQL Server™ 2000 将默认值装载到该列中。

说明 也可以使用 INSERT STATEMENT 语句 DEFAULT VALUES 子句显式要求 SQL Server 将默认值插入列中。

如果列不允许空值且没有 DEFAULT 定义，就必须明确地指定列值，否则 SQL Server 会返回错误信息，指出该列不允许空值。

将 DEFAULT 定义、为空性和对列值的插入归纳如下：

列定义 | 无输入， 无 DEFAULT 定义 | 无输入， DEFAULT 定义 | 输入空值
允许空值 | NULL | 默认值 | NULL
不允许空值 | 错误 | 默认值 | 错误

DEFAULT 对象

DEFAULT 对象是为特定数据库定义的。通过将其绑定到默认值要应用的每个列上，它可为不同表的列所共享。例如，如果多个表中均有 quantity 列，则可以在数据库中定义 DEFAULT 对象，当用户在任何表的 quantity 列中未输入值时在该列中插入数值 1。

如果 DEFAULT 对象绑定到某列上，可在特定的表中为该列指定不同的默认值。这样在向列绑定新的默认值之前从该列中释放已有的 DEFAULT 对象。

请参见

允许空值

创建和修改 DEFAULT 定义


允许空值

列为空性决定该列在表中是否允许空值。空值或 NULL 并不等于零 (0)、空白或零长度的字符串（如""），NULL 意味着没有输入。NULL 的存在通常表明值未知或未定义。例如，pubs 数据库 titles 表中 price 列的空值并不表示该书没有价格，而是指其价格未知或尚未设定。总之，由于空值在查询和更新时会使事情变得更复杂，而且有其它列选项，如 PRIMARY KEY 约束等不能使用允许空值的列，所以应避免允许空值。

如果插入某行但没有为允许空值的列包括值，则 Microsoft® SQL Server™ 2000 提供 NULL 值（除非存在 DEFAULT 定义或 DEFAULT 对象）。用关键字 NULL 定义的列也接受用户的 NULL 显式输入，不论它是何种数据类型或是否有默认值与之关联。NULL 值不应放在引号内，否则会被解释为字符串"NULL"而不是空值。

指定一列不允许空值而确保行中一列永远包含数据可以保持数据的完整性。如果不允许空值，用户在向表中写数据时必须在列中输入一个值，否则该行不被接收入数据库。

说明 定义了 PRIMARY KEY 约束或 IDENTITY 属性的列不允许空值。

请参见

空值

列属性


创建和修改表

设计完数据库后就可创建数据库中将存储数据的表。数据通常存储在永久表中。表存储在数据库文件中，任何有相应权限的用户都可以对之进行操作，除非它已被删除。

临时表

也可以创建临时表。临时表与永久表相似，但临时表存储在 tempdb 中，当不再使用时会自动删除。

有本地和全局两种类型的临时表，二者在名称、可见性和可用性上均不相同。本地临时表的名称以单个数字符号 (#) 打头；它们仅对当前的用户连接是可见的；当用户从 Microsoft® SQL Server™ 2000 实例断开连接时被删除。全局临时表的名称以数学符号 (##) 打头，创建后对任何用户都是可见的，当所有引用该表的用户从 SQL Server 断开连接时被删除。

例如，如果创建名为 employees 的表，则任何人只要在数据库中有使用该表的安全权限就可以使用该表，除非它已删除。如果创建名为 #employees 的本地临时表，只有您能对该表执行操作且在断开连接时该表删除。如果创建名为 ##employees 的全局临时表，数据表中的任何用户均可对该表执行操作。如果该表在您创建后没有其他用户使用，则当您断开连接时该表删除。如果该表在您创建后有其他用户使用，则 SQL Server在所有用户断开连接后删除该表。

表的属性

每个表至多可定义 1024 列。表和列的命名要遵守标识符的规定，在特定表中必须是唯一的，但同一数据库中的不同表可使用相同的列名。必须为每列指定数据类型。

尽管对于每一个所有者在一个数据库内表的名称必须是唯一的，但如果为每张表指定不同的所有者，则可以创建多个具有相同名称的表。可以创建名为 employees 的两个表并分别指定 Jonah 和 Sally 作为其所有者。当您要使用某一 employees 表时，可以通过指定表的使用者以及表名称来区分两个表。

创建表

Transact-SQL

企业管理器

SQL-DMO

修改表

表创建之后可以修改许多已定义的选项，包括：

  • 添加、修改、删除列。例如，列的名称、长度、数据类型、精度、小数位数以及为空性均可进行修改，不过有一些限制而已。有关更多信息，请参见修改列属性。

  • 可添加或删除 PRIMARY KEY 和 FOREIGN KEY 约束。

  • 可添加或删除 UNIQUE 和 CHECK 约束及 DEFAULT 定义（对象）。

  • 可使用 IDENTITY 或 ROWGUIDCOL 属性添加或删除标识符列。虽然 ROWGUIDCOL 属性也可添加至现有列或从现有列删除，但是任何时候在表中只能有一列可具有该属性。

  • 表及表中所选定的列已注册为全文索引。

有关修改表的更多信息，请参见 ALTER TABLE。

表名及表的所有者也可更改。在执行本操作时，如果触发器、存储过程、Transact-SQL 脚本及其它程序代码中使用了旧的表名或所有者名，也应随之更改。

重命名表

Transact-SQL

企业管理器

SQL-DMO

更改表的所有者

Transact-SQL

SQL-DMO

请参见

指定列数据类型

使用标识符

在文件组上放置表


修改列属性

表的每一列都有一组属性，如名称、数据类型、为空性和数据长度等。列的所有属性构成列的定义。

可以使用数据库关系图在数据库表中直接指定列的属性。在数据库中创建表之前列应具备三个属性：

  • 列名

  • 数据类型

  • 数据长度

可以通过重命名、更改长度或指定默认值等来修改列的属性。

列的数据类型

如果列中的已有数据可以隐式转换为新的数据类型，则可对该列的数据类型进行修改。有关更多信息，请参见 ALTER TABLE。

列的数据长度

选择数据类型时将自动定义长度。对于 binary、char、nchar、varbinary、varchar 或 nvarchar 数据类型的列只能增加或减少其长度属性。对于其它数据类型的列，其长度由数据类型确定，不可更改。如果新指定的长度比原列宽小，则超过新列宽的数据将在不提示的情况下被截断。定义了 PRIMARY KEY 或 FOREIGN KEY 约束的列的宽度不能更改。

说明 当使用 SQL Server 企业管理器保存表或数据库的关系图时，对列数据长度的修改将会导致在数据库中重新创建表。

列精度

数值列的精度是指选定数据类型所使用的最大位数。非数值列的精度指最大列宽或定义的列宽。

除 decimal 和 numeric 外，其它数据类型的精度自动定义。如果要重新定义数值列的最大位数，可以更改 decimal 和 numeric 数据类型的精度。SQL Server 企业管理器不允许更改不是这些所指定数据类型的列的精度。

说明 当使用 SQL Server 企业管理器保存表或数据库的关系图时，对列精度的修改将会导致在数据库中重新创建表。

列的小数位数

numeric 或 decimal 列的小数位数是指小数点右边的最大位数。在选定数据类型时，列的小数位数默认设置为 0。对于包含近似浮点数的列，由于小数点右边的位数不固定，故其小数位数并未定义。如果要重新定义小数点右边的位数，可以更改 numeric 或 decimal 列的小数位数。

说明 当使用 SQL Server 企业管理器保存表或数据库的关系图时，对列小数位数的修改将会导致在数据库中重新创建表。

列的为空性

列可以定义为允许或不允许空值。默认情况下，列允许空值。只有当现有列不存在空值及该列不存在索引时，才可以将该列更改为不允许空值。要使含有空值的列不允许空值：

  1. 添加具有 DEFAULT 定义的新列，插入有效值代替空值。

  2. 将原有列中的数据复制到新列中。

  3. 删除旧列。

只要列没有 PRIMARY KEY 约束就可以由不允许空值更改为允许空值。

说明 当使用 SQL Server 企业管理器保存表或数据库的关系图时，对新的、非键列为空性的修改将会导致在数据库中重新创建表。

设置列属性

Transact-SQL

企业管理器

SQL-DMO

查看列属性

Transact-SQL

企业管理器

SQL-DMO

重命名列

Transact-SQL

企业管理器

SQL-DMO

请参见

使用表

数据库对象

精度、小数位数和长度


添加和删除列

如果列允许空值或在列上创建了 DEFAULT 约束，则 Microsoft® SQL Server™ 2000 允许向现有表中添加列。向表中添加新列时，SQL Server 为表中每个现有的数据行在列中插入一个值。因此，在向表中添加列时向列添加 DEFAULT 定义会很有用。如果新列没有 DEFAULT 定义，则必须指定它允许空值。如果新列不允许空值，则 SQL Server 向其中插入空值或返回错误。

同样，列可从现有表中删除。但下列情况不可删除列：

  • 正在复制。

  • 用在索引中的列。

  • 用在 CHECK、FOREIGN KEY、UNIQUE 或 PRIMARY KEY 约束中的列。

  • 与 DEFAULT 定义关联或绑定到某一默认对象。

  • 绑定到规则的列。

  • 已注册为全文本支持。

  • 用作表的全文键。

添加或删除列

Transact-SQL

企业管理器

SQL-DMO

将列从一个表复制到另一个表

企业管理器


创建和修改 PRIMARY KEY 约束

一个 PRIMARY KEY 约束可以：

  • 作为表定义的一部分在创建表时创建。

  • 添加到尚没有 PRIMARY KEY 约束的表中（一个表只能有一个 PRIMARY KEY 约束）。

  • 如果已有 PRIMARY KEY 约束，则可对其进行修改或删除。例如，可以使表的 PRIMARY KEY 约束引用其它列，更改列的顺序、索引名、聚集选项或 PRIMARY KEY 约束的填充因子。定义了 PRIMARY KEY 约束的列的列宽不能更改。

    说明 若要使用 Transact-SQL 或 SQL-DMO 修改 PRIMARY KEY，必须先删除现有的 PRIMARY KEY 约束，然后再用新定义重新创建。

当向表中的现有列添加 PRIMARY KEY 约束时，Microsoft® SQL Server™ 2000 检查列中现有的数据以确保现有数据遵从主键的规则：

  • 无空值

  • 无重复值

如果 PRIMARY KEY 约束添加到具有空值或重复值的列上，SQL Server 不执行该操作并返回错误信息。不能添加违背上述规定的 PRIMARY KEY 约束。

SQL Server 自动创建唯一的索引来强制 PRIMARY KEY 约束所要求的唯一性。如果表中不存在聚集索引或未明确指定非聚集索引，则将创建唯一的聚集索引强制 PRIMARY KEY 约束。

重要 当 PRIMARY KEY 约束由另一表的 FOREIGN KEY 约束引用时，不能删除 PRIMARY KEY 约束；要删除它，必须先删除 FOREIGN KEY 约束。

在创建表时创建 PRIMARY KEY 约束

Transact-SQL

SQL-DMO

在现有表中创建或删除 PRIMARY KEY 约束

Transact-SQL

企业管理器

SQL-DMO

修改 PRIMARY KEY 约束

企业管理器

请参见

主键约束


创建和修改 FOREIGN KEY 约束

FOREIGN KEY 约束可以：

  • 作为表定义的一部分在创建表时创建。

  • 如果 FOREIGN KEY 约束与另一个表（或同一表）已有的 PRIMARY KEY 约束或 UNIQUE 约束相关联，则可向现有表添加 FOREIGN KEY 约束。一个表可以有多个 FOREIGN KEY 约束。

  • 对已有的 FOREIGN KEY 约束进行修改或删除。例如，要使一个表的 FOREIGN KEY 约束引用其它列。定义了 FOREIGN KEY 约束列的列宽不能更改。

    说明 若要使用 Transact-SQL 或 SQL-DMO修改 FOREIGN KEY 约束，必须首先删除已有的 FOREIGN KEY 约束，然后再通过新定义重新创建。

当向表的现有列添加 FOREIGN KEY 约束时，默认情况下 Microsoft® SQL Server™ 2000 检查列中的现有数据，以确保除 NULL 外的所有数据存在于被引用的 PRIMARY KEY 或 UNIQUE 约束的列中。不过，SQL Server 也可不对列数据的新约束进行检查，不考虑列中的数据而添加新约束。当现有数据已符合新的 FOREIGN KEY 约束，或业务规则要求从此开始强制约束时，使用此选项比较有用。

不过，添加约束而不进行数据检查时一定要注意，因为这样放弃了 SQL Server 对表中数据完整性的控制。

禁用 FOREIGN KEY 约束

下列情况可以禁用 FOREIGN KEY 约束：

  • INSERT 和 UPDATE 语句

    这种情况允许不经约束验证就可修改表中的数据。在执行 INSERT 和 UPDATE 语句过程中，如果新数据违反约束或约束只应适用于数据库中已有的数据，那么禁用 FOREIGN KEY 约束。

  • 复制处理。

    如果一个 FOREIGN KEY 约束为源数据库特有，请在复制时禁用该约束。复制表时，表定义和数据从源数据库复制到目的数据库。这两个数据库通常（但不一定）在不同的服务器上。如果 FOREIGN KEY 约束为源数据库特有而在复制时未禁用，则有可能无谓地妨碍向目标数据库输入数据。

删除 FOREIGN KEY 约束，从而删除了强制外键列和另一表中相关主键（或 UNIQUE 约束）列的引用完整性要求。

在创建表时创建 FOREIGN KEY 约束

Transact-SQL

SQL-DMO

在已有表中创建 FOREIGN KEY 约束

Transact-SQL

企业管理器

SQL-DMO

创建 FOREIGN KEY 约束时不对现有数据进行检查

Transact-SQL

企业管理器

SQL-DMO

修改 FOREIGN KEY 约束

企业管理器

对 INSERT 和 UPDATE 语句禁用 FOREIGN KEY 约束

Transact-SQL

企业管理器

SQL-DMO

复制时禁用 FOREIGN KEY 约束

Transact-SQL

企业管理器

SQL-DMO

删除 FOREIGN KEY 约束

Transact-SQL

企业管理器

SQL-DMO

请参见

FOREIGN KEY 约束


创建和修改 UNIQUE 约束

UNIQUE 约束可以：

  • 作为表定义的一部分在创建表时创建。

  • 如果组成 UNIQUE 约束的列或列组合只包含唯一值或 NULL 值，则可向现有表添加 UNIQUE 约束。一个表可含有多个 UNIQUE 约束。

  • 修改或删除已有的 CHECK 约束。例如，可能要使表的 UNIQUE 约束引用其它列或要更改聚集索引的类型。

    说明 若要使用 Transact-SQL 或 SQL-DMO修改 UNIQUE 约束，必须首先删除现有的 UNIQUE 约束，然后再通过新定义重新创建。

在向表中的现有列添加 UNIQUE 约束时，默认情况下 Microsoft® SQL Server™ 2000 检查列中的现有数据，确保除 NULL 外的所有值均唯一。如果对有重复值的列添加 UNIQUE 约束，SQL Server 将返回错误信息并不添加约束。

SQL Server 自动创建 UNIQUE 索引来强制 UNIQUE 约束的唯一性要求。因此，如果试图插入重复行，SQL Server 将返回错误信息，说明该操作违反了 UNIQUE 约束并不将该行添加到表中。除非明确指定了聚集索引，否则，默认情况下创建唯一的非聚集索引以强制 UNIQUE 约束。

删除 UNIQUE 约束，以删除对约束中所包含列或列组合输入值的唯一性要求。如果相关列是表的全文键，则不能删除 UNIQUE 约束。

在创建表时创建 UNIQUE 约束

Transact-SQL

企业管理器

在现有的表中创建 UNIQUE 约束

Transact-SQL

企业管理器

SQL-DMO

修改 UNIQUE 约束

企业管理器

删除 UNIQUE 约束

Transact-SQL

企业管理器

SQL-DMO

请参见

UNIQUE 约束


创建和修改 CHECK 约束

CHECK 约束可以：

  • 作为表定义的一部分在创建表时创建。

  • 添加到现有表中。表和列可以包含多个 CHECK 约束。

  • 修改或删除现有的 CHECK 约束。例如，可以修改表中列的CHECK 约束的表达式。

    说明 若要使用 Transact-SQL 或 SQL-DMO修改 CHECK 约束，必须首先删除已有的 CHECK 约束，然后再通过新定义重新创建。

在现有表中添加 CHECK 约束时，该约束可以仅作用于新数据也可以同时作用与现有的数据。默认设置为 CHECK 约束同时作用于现有数据和新数据。当现有数据已符合新的 CHECK 约束，或业务规则要求从此开始强制约束时，使用约束仅作用于新数据选项比较有用。

例如，旧约束要求邮政编码为 5 位，而新约束要求为 9 位。5 位的旧邮政编码依然有效并与 9 位的新邮政编码共存。因此，只须对新邮政编码按新的约束进行检验。

不过，添加约束而不检查现有数据时一定要小心，因为这样做将放弃 Microsoft® SQL Server™ 2000 对表强制完整性规则的控制。

禁用 CHECK 约束

下列情况可以禁用现有 CHECK 约束：

  • INSERT 和 UPDATE 语句，以允许不经约束确认修改表中的数据。在执行 INSERT 和 UPDATE 语句时，如果新数据违反约束或约束应只适用于数据库中已有的数据，那么可禁用 CHECK 约束。

  • 复制处理。如果该约束为源数据库所特有，则在复制时请禁用 CHECK 约束。复制表时，表定义和数据从源数据库复制到目标数据库。这两个数据库通常（但不一定）在不同的服务器上。如果源数据库特有的 CHECK 约束在复制时未禁用，则可能无谓地妨碍向目标数据库输入新数据。

删除 CHECK 约束以取消对约束表达式所包含列在接受数据时的限制。

在创建表时创建 CHECK 约束

Transact-SQL

SQL-DMO

在现有的表中创建 CHECK 约束

Transact-SQL

企业管理器

SQL-DMO

在创建 CHECK 约束时不对现有数据进行检查

Transact-SQL

企业管理器

SQL-DMO

修改 CHECK 约束

企业管理器

执行 INSERT 和 UPDATE 语句时禁用 CHECK 约束

Transact-SQL

企业管理器

SQL-DMO

复制时禁用 CHECK 约束

Transact-SQL

企业管理器

SQL-DMO

删除 CHECK 约束

Transact-SQL

企业管理器

SQL-DMO

请参见

CHECK 约束


创建和修改 DEFAULT 定义

DEFAULT 定义可以：

  • 作为表定义的一部分在创建表时创建。

  • 添加到现有表中。表的每一列都可包含一个 DEFAULT 定义。

  • 修改或删除现有的 DEFAULT 定义。例如，当没有数据输入时可以修改列中插入的值。

    说明 若要使用 Transact-SQL 或 SQL-DMO修改 DEFAULT 定义，必须首先删除已有的 DEFAULT 定义，然后通过新定义重新创建。

如下定义的列不能创建 DEFAULT 定义：

  • timestamp 数据类型。

  • IDENTITY 或 ROWGUIDCOL 属性。

  • 已有 DEFAULT 定义或 DEFAULT 对象。

说明 默认值必须与 DEFAULT 定义适用的列的数据类型相一致。例如，int 列的默认值必须是整数，而不是字符串。

在对表中现有列添加 DEFAULT 定义时，默认情况下 Microsoft® SQL Server™ 2000 只对添加到表中的新数据行应用新默认值；而使用以前的 DEFAULT 定义插入的现有数据不受影响。不过，当在已有表中添加新列时，可指定 SQL Server 对表中已有行的新列插入默认值（由 DEFAULT 定义指定），而不是插入空值。

删除 DEFAULT 定义后，如果新行中的该列未输入数值，则 SQL Server 自动插入空值而非默认值。不过，表中的已有数据保持不变。

在创建表时创建列的 DEFAULT 定义

Transact-SQL

企业管理器

SQL-DMO

在现有表中创建或删除列的 DEFAULT 定义

Transact-SQL

企业管理器

SQL-DMO

创建 DEFAULT 对象

Transact-SQL

企业管理器

企业管理器

SQL-DMO

删除 DEFAULT 对象

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

DEFAULT 定义


创建和修改标识符列

每个表中只能创建一个标识符列和一个全局唯一标识符列。

IDENTITY 属性

标识符列可用 IDENTITY 属性建立，使应用程序开发人员得以对表中所插入的第一行指定标识数字（Identity Seed 属性），并确定要添加到种子上的增量（Identity Increment 属性）以决定后面的标识数字。在向具有标识符列的表中插入值时，Microsoft® SQL Server™ 2000 通过递增种子值的方法自动生成下一个标识值。

在用 IDENTITY 属性定义标识符列时应注意以下几点：

  • 一个表只能有一列定义为 IDENTITY 属性，而且该列必须以 decimal、int、numeric、smallint、bigint 或 tinyint 数据类型定义。

  • 可指定种子和增量值。二者的默认值均为 1。

  • 标识符列不允许空值，也不能包含 DEFAULT 定义或对象。

  • 在设置 DENTITY 属性后，可以使用 IDENTITYCOL 关键字在选择表中引用该列。

  • 可以使用 OBJECTPROPERTY 函数确定一个表是否具有 IDENTITY（标识）列，使用 COLUMNPROPERTY 函数确定 IDENTITY 列的名称。
全局唯一标识符

尽管 IDENTITY 属性自动为表生成行号，但不同表的标识符列可以生成相同的行号。这是因为 IDENTITY 属性只须在所使用的表上保持唯一。如果应用程序需要生成在整个数据库或世界各地所有网络计算机的全部数据库中均为唯一的标识符列，请使用 ROWGUIDCOL 属性、uniqueidentifier 数据类型和 NEWID 函数。

使用 ROWGUIDCOL 属性定义全局唯一标识符列时应注意；

  • 一个表只能有一个 ROWGUIDCOL 列，且该列必须定义为 uniqueidentifier 数据类型。

  • SQL Server 不为该列自动生成值。若要插入全局唯一数值，应为列创建 DEFAULT 定义，以使用 NEWID 功能生成全局唯一数值。

  • 在设置 ROWGUIDCOL 属性后，使用 ROWGUIDCOL 关键字可在选择列表中引用该列。这与使用 IDENTITYCOL 关键字引用 IDENTITY 列相似。

  • 可使用 OBJECTPROPERTY 函数确定表是否含有 ROWGUIDCOL 列，使用 COLUMNPROPERTY 函数确定 ROWGUIDCOL 列的名称。

  • 由于 ROWGUIDCOL 属性不强制唯一性，所以应使用 UNIQUE 约束以确保插入 ROWGUIDCOL 列的值是唯一的。

说明 如果表的标识符列经常执行删除操作，标识值间可能会产生断缺，删除的标识值不能再使用。要避免出现这类断缺，请勿使用 IDENTITY 属性。而应在插入行时，以标识符列中现有的数值为基础创建确定新标识值的触发器。

在创建表时创建新的标识符列

Transact-SQL

企业管理器

SQL-DMO

在现有表中创建新的标识符列

Transact-SQL

企业管理器

SQL-DMO

删除标识符列

Transact-SQL

企业管理器

SQL-DMO

请参见

自动编号和标识符列

COLUMNPROPERTY

NEWID

OBJECTPROPERTY

uniqueidentifier

使用 Uniqueidentifier 数据


查看表

在数据库中创建表之后，可能需要查看有关表属性的一些信息（如列名、列的数据类型及其索引的特性等等）。而最重要的是需要查看表中的数据。

还可以显示表的相关性来确定哪些对象（如视图、存储过程和触发器）是由表决定的。在更改表时，相关对象可能会受到影响。

查看表定义

Transact-SQL

企业管理器

SQL-DMO

查看表中的数据

Transact-SQL

企业管理器

查看表的相关性

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看列属性

Transact-SQL

企业管理器

SQL-DMO

查看外键特性

如果想知道哪些列在关系中充当外键时，请查看关系的外键属性。如果外键列与主键相关联，数据库关系图中在行选择器中用主键符号标识主键列。

查看关系的外键特性

Transact-SQL

企业管理器

SQL-DMO

查看表的约束

Transact-SQL

SQL-DMO

请参见

查询基础知识

查看索引


删除表

有时需要删除表（如当要实现新的设计或释放数据库的空间时）。删除表时，表的结构定义、数据、全文索引、约束和索引都永久地从数据库中删除，原来存放表及其索引的存储空间可用来存放其它表。如果不想等待临时表自动除去，可明确删除临时表。

如果要删除通过 FOREIGN KEY 和 UNIQUE 或 PRIMARY KEY 约束相关的表，必须首先删除具有 FOREIGN KEY 约束的表。如果要删除 FOREIGN KEY 约束中引用的表而不是删除整个外键表，则必须删除 FOREIGN KEY 约束。

删除表

Transact-SQL

企业管理器

SQL-DMO

删除 FOREIGN KEY 约束

Transact-SQL

企业管理器

SQL-DMO


索引

数据库中的索引与书籍中的索引类似。在一本书中，利用索引可以快速查找所需信息，无须阅读整本书。在数据库中，索引使数据库程序无须对整个表进行扫描，就可以在其中找到所需数据。书中的索引是一个词语列表，其中注明了包含各个词的页码。而数据库中的索引是一个表中所包含的值的列表，其中注明了表中包含各个值的行所在的存储位置。可以为表中的单个列建立索引，也可以为一组列建立索引；索引采用 B 树结构。索引包含一个条目，该条目有来自表中每一行的一个或多个列（搜索关键字）。B 树按搜索关键字排序，可以在搜索关键字的任何子词条集合上进行高效搜索。例如，对于一个 A、B、C 列上的索引，可以在 A 以及 A、B 和 A、B、C 上对其进行高效搜索。

大多数书中包含一个关于词汇、名称、地点等等的总索引。数据库则包含分别关于所选类型或数据列的索引：这好比在一本书中分别为人名和地名建立索引。当创建数据库并优化其性能时，应该为数据查询所使用的列创建索引。

在随 Microsoft® SQL Server™ 2000 提供的 pubs 示例数据库中，employee 表在 emp_id 列上有一个索引。下面的示意图显示索引如何存储每个 emp_id 值并指向表中包含各个值的数据行。

当 SQL Server 执行一个语句，在 employee 表中根据指定的 emp_id 值查找数据时，它能够识别 emp_id 列的索引，并使用该索引查找所需数据。如果该索引不存在，它会从表的第一行开始，逐行搜索指定的 emp_id 值。

SQL Server 为某些类型的约束（如 PRIMARY KEY 和 UNIQUE 约束）自动创建索引。可以通过创建不依赖于约束的索引，进一步对表定义进行自定义。

不过，索引为性能所带来的好处却是有代价的。带索引的表在数据库中会占据更多的空间。另外，为了维护索引，对数据进行插入、更新、删除操作的命令所花费的时间会更长。在设计和创建索引时，应确保对性能的提高程度大于在存储空间和处理资源方面的代价。

请参见

全文索引

索引优化向导

索引优化建议

表和索引构架


设计索引

当 Microsoft® SQL Server™ 2000 执行查询时，查询优化器会对可用的数据检索方法的成本进行评估，从中选用最有效的方法。SQL Server 可以扫描表，如果索引存在则使用索引。当 SQL Server 执行表扫描时，它从表的第一行开始逐行查找，将符合查询条件的行提取出来。当 SQL Server 使用索引时，它会查找查询所需的行的存储位置，并只提取出所需的行。

在考虑是否为一个列创建索引时，应考虑被索引的列是否以及如何用于查询中。索引对下列查询很有帮助：

  • 搜索符合特定搜索关键字值的行（精确匹配查询）。精确匹配比较是指查询使用 WHERE 语句指定具有给定值的列条目。例如：
    WHERE emp_id = 'VPA30890F'
  • 搜索其搜索关键字值为范围值的行（范围查询）。范围查询是指查询指定其值介于两个值之间的任何条目。例如：
    WHERE job_lvl BETWEEN 9 and 12

    或

    WHERE job_lvl >= 9 and job_lvl <= 12
  • 在表 T1 中搜索根据联接谓词与表 T2 中的某个行匹配的行（索引嵌套循环联接）。

  • 在不进行显式排序操作的情况下产生经排序的查询输出，尤其是经排序的动态游标。

  • 在不进行显式排序操作的情况下，按一种有序的顺序对行进行扫描，以允许基于顺序的操作，如合并联接和流聚合。

  • 以优于表扫描的性能对表中所有的行进行扫描，性能提高是由于减少了要扫描的列集和数据总量（该查询有覆盖索引可供使用）。

  • 搜索插入和更新操作中重复的新搜索关键字值，以实施 PRIMARY KEY 和 UNIQUE 约束。

  • 搜索已定义了 FOREIGN KEY 约束的两个表之间匹配的行。

使用 LIKE 比较进行查询时，如果模式以特定字符串如"abc%"开头，使用索引则会提高效率；如果模式以通配符如"%xyz"开头，则索引不起作用。

在很多查询中，索引可以带来多方面的好处。例如，索引除了可以覆盖查询外，还使得可以进行范围查询。SQL Server 可以在同一个查询中为一个表使用多个索引，并可以合并多个索引（使用联接算法），以便搜索关键字共同覆盖一个查询。另外，SQL Server 会自动确定利用哪些索引进行查询，并且能够在表被改动时确保该表的所有索引都得到维护。

其它索引设计准则

设计索引时还要考虑的其它准则包括：

  • 一个表如果建有大量索引会影响 INSERT、UPDATE 和 DELETE 语句的性能，因为在表中的数据更改时，所有索引都须进行适当的调整。另一方面，对于不需要修改数据的查询（SELECT 语句），大量索引有助于提高性能，因为 SQL Server 有更多的索引可供选择，以便确定以最快速度访问数据的最佳方法。

  • 覆盖的查询可以提高性能。覆盖的查询是指查询中所有指定的列都包含在同一个索引中。例如，如果在一个表的 a、b 和 c 列上创建了组合索引，则从该表中检索 a 和 b 列的查询被视为覆盖的查询。创建覆盖一个查询的索引可以提高性能，因为该查询的所有数据都包含在索引自身当中；检索数据时只需引用表的索引页，不必引用数据页，因而减少了 I/O 总量。尽管给索引添加列以覆盖查询可以提高性能，但在索引中额外维护更多的列会产生更新和存储成本。

  • 对小型表进行索引可能不会产生优化效果，因为 SQL Server 在遍历索引以搜索数据时，花费的时间可能会比简单的表扫描还长。

  • 应使用 SQL 事件探查器和索引优化向导帮助分析查询，确定要创建的索引。为数据库及其工作负荷选择正确的索引是非常复杂的，需要在查询速度和更新成本之间取得平衡。窄索引（搜索关键字中只有很少的列的索引）需要的磁盘空间和维护开销都更少。而另一方面，宽索引可以覆盖更多的查询。确定正确的索引集没有简便的规则。经验丰富的数据库管理员常常能够设计出很好的索引集，但是，即使对于不特别复杂的数据库和工作负荷来说，这项任务也十分复杂、费时和易于出错。可以使用索引优化向导使这项任务自动化。有关更多信息，请参见索引优化向导。

  • 可以在视图上指定索引。有关更多信息，请参见设计索引视图。

  • 可以在计算列上指定索引。有关更多信息，请参见在计算列上创建索引。
索引的特征

在确定某一索引适合某一查询之后，可以自定义最适合具体情况的索引类型。索引特征包括：

  • 聚集还是非聚集

  • 唯一还是不唯一

  • 单列还是多列

  • 索引中的列顺序为升序还是降序

  • 覆盖还是非覆盖

还可以自定义索引的初始存储特征，通过设置填充因子优化其维护，并使用文件和文件组自定义其位置以优化性能。

请参见

设计表

填充因子

在文件组上放置索引

查询优化

了解合并联接

了解嵌套循环联接


使用聚集索引

聚集索引确定表中数据的物理顺序。聚集索引类似于电话簿，后者按姓氏排列数据。由于聚集索引规定数据在表中的物理存储顺序，因此一个表只能包含一个聚集索引。但该索引可以包含多个列（组合索引），就像电话簿按姓氏和名字进行组织一样。

聚集索引对于那些经常要搜索范围值的列特别有效。使用聚集索引找到包含第一个值的行后，便可以确保包含后续索引值的行在物理相邻。例如，如果应用程序执行的一个查询经常检索某一日期范围内的记录，则使用聚集索引可以迅速找到包含开始日期的行，然后检索表中所有相邻的行，直到到达结束日期。这样有助于提高此类查询的性能。同样，如果对从表中检索的数据进行排序时经常要用到某一列，则可以将该表在该列上聚集（物理排序），避免每次查询该列时都进行排序，从而节省成本。

当索引值唯一时，使用聚集索引查找特定的行也很有效率。例如，使用唯一雇员 ID 列 emp_id 查找特定雇员的最快速的方法，是在 emp_id 列上创建聚集索引或 PRIMARY KEY 约束。

说明 如果该表上尚未创建聚集索引，且在创建 PRIMARY KEY 约束时未指定非聚集索引，PRIMARY KEY 约束会自动创建聚集索引。

也可以在 lname（姓氏）列和 fname（名字）列上创建聚集索引，因为雇员记录常常是按姓名而不是按雇员 ID 分组和查询的。

注意事项

定义聚集索引键时使用的列越少越好，这一点很重要。如果定义了一个大型的聚集索引键，则同一个表上定义的任何非聚集索引都将增大许多，因为非聚集索引条目包含聚集键。当把 SQL 脚本保存到可用空间不足的磁盘上时，索引优化向导不返回错误。有关 Microsoft® SQL Server™ 2000 中如何实现非聚集索引的更多信息，请参见非聚集索引。

在分析过程中，索引优化向导会消耗相当多的 CPU 及内存资源。最好在生产服务器的测试版上执行优化，而不要在生产服务器上执行。此外，最好在另一台计算机上而非运行 SQL Server 的计算机上运行该向导。该向导不能用于在 SQL Server 6.5 版或更早版本的数据库中选择或创建索引及统计信息。

在创建聚集索引之前，应先了解您的数据是如何被访问的。可考虑将聚集索引用于：

  • 包含大量非重复值的列。

  • 使用下列运算符返回一个范围值的查询：BETWEEN、>、>=、< 和 <=。

  • 被连续访问的列。

  • 返回大型结果集的查询。

  • 经常被使用联接或 GROUP BY 子句的查询访问的列；一般来说，这些是外键列。对 ORDER BY 或 GROUP BY 子句中指定的列进行索引，可以使 SQL Server 不必对数据进行排序，因为这些行已经排序。这样可以提高查询性能。

  • OLTP 类型的应用程序，这些程序要求进行非常快速的单行查找（一般通过主键）。应在主键上创建聚集索引。

聚集索引不适用于：

  • 频繁更改的列

    这将导致整行移动（因为 SQL Server 必须按物理顺序保留行中的数据值）。这一点要特别注意，因为在大数据量事务处理系统中数据是易失的。

  • 宽键

    来自聚集索引的键值由所有非聚集索引作为查找键使用，因此存储在每个非聚集索引的叶条目内。

请参见

聚集索引

创建索引

创建和修改 PRIMARY KEY 约束


使用非聚集索引

非聚集索引与课本中的索引类似。数据存储在一个地方，索引存储在另一个地方，索引带有指针指向数据的存储位置。索引中的项目按索引键值的顺序存储，而表中的信息按另一种顺序存储（这可以由聚集索引规定）。如果在表中未创建聚集索引，则无法保证这些行具有任何特定的顺序。

与使用书中索引的方式相似，Microsoft® SQL Server™ 2000 在搜索数据值时，先对非聚集索引进行搜索，找到数据值在表中的位置，然后从该位置直接检索数据。这使非聚集索引成为精确匹配查询的最佳方法，因为索引包含描述查询所搜索的数据值在表中的精确位置的条目。如果基础表使用聚集索引排序，则该位置为聚集键值；否则，该位置为包含行的文件号、页号和槽号的行 ID (RID)。例如，对于在 emp_id 列上有非聚集索引的表，如要搜索其雇员 ID (emp_id)，SQL Server 会在索引中查找这样一个条目，该条目精确列出匹配的 emp_id 列在表中的页和行，然后直接转到该页该行。

多个非聚集索引

有些书籍包含多个索引。例如，一本介绍园艺的书可能会包含一个植物通俗名称索引，和一个植物学名索引，因为这是读者查找信息的两种最常用的方法。对于非聚集索引也是如此。可以为在表中查找数据时常用的每个列创建一个非聚集索引。

注意事项

在创建非聚集索引之前，应先了解您的数据是如何被访问的。可考虑将非聚集索引用于：

  • 包含大量非重复值的列，如姓氏和名字的组合（如果聚集索引用于其它列）。如果只有很少的非重复值，如只有 1 和 0，则大多数查询将不使用索引，因为此时表扫描通常更有效。

  • 不返回大型结果集的查询。

  • 返回精确匹配的查询的搜索条件（WHERE 子句）中经常使用的列。

  • 经常需要联接和分组的决策支持系统应用程序。应在联接和分组操作中使用的列上创建多个非聚集索引，在任何外键列上创建一个聚集索引。

  • 在特定的查询中覆盖一个表中的所有列。这将完全消除对表或聚集索引的访问。

请参见

创建索引

索引优化向导

非聚集索引


使用唯一索引

唯一索引可以确保索引列不包含重复的值。在多列唯一索引的情况下，该索引可以确保索引列中每个值组合都是唯一的。例如，如果在 last_name、first_name 和 middle_initial 列的组合上创建了唯一索引 full_name，则该表中任何两个人都不可以具有相同的全名。

聚集索引和非聚集索引都可以是唯一的。因此，只要列中的数据是唯一的，就可以在同一个表上创建一个唯一的聚集索引和多个唯一的非聚集索引。

注意事项

只有当唯一性是数据本身的特征时，指定唯一索引才有意义。如果必须实施唯一性以确保数据的完整性，则应在列上创建 UNIQUE 或 PRIMARY KEY 约束，而不要创建唯一索引。例如，如果打算经常查询雇员表（主键为 emp_id）中的社会安全号码 (ssn) 列，并希望确保社会安全号码的唯一性，则在 ssn 列上创建 UNIQUE 约束。如果用户为一个以上的雇员输入了同一个社会安全号码，则会显示错误。

说明 创建 PRIMARY KEY 或 UNIQUE 约束会在表中指定的列上自动创建唯一索引。

在同一个列组合上创建唯一索引而不是非唯一索引可为查询优化器提供附加信息；所以最好创建唯一索引。

请参见

创建索引

索引优化向导


填充因子

在创建聚集索引时，表中的数据按照索引列中的值的顺序存储在数据库的数据页中。在表中插入新的数据行或更改索引列中的值时，Microsoft® SQL Server™ 2000 可能必须重新组织表中的数据存储，以便为新行腾出空间，保持数据的有序存储。这同样适用于非聚集索引。添加或更改数据时，SQL Server 可能不得不重新组织非聚集索引页中的数据存储。向一个已满的索引页添加某个新行时，SQL Server 把大约一半的行移到新页中以便为新行腾出空间。这种重组称为页拆分。页拆分会降低性能并使表中的数据存储产生碎片。有关更多信息，请参见表和索引构架。

创建索引时，可以指定一个填充因子，以便在索引的每个叶级页上留出额外的间隙和保留一定百分比的空间，供将来表的数据存储容量进行扩充和减少页拆分的可能性。填充因子的值是从 0 到 100 的百分比数值，指定在创建索引后对数据页的填充比例。值为 100 时表示页将填满，所留出的存储空间量最小。只有当不会对数据进行更改时（例如，在只读表中）才会使用此设置。值越小则数据页上的空闲空间越大，这样可以减少在索引增长过程中对数据页进行拆分的需要，但需要更多的存储空间。当表中数据会发生更改时，这种设置更为适当。

提供填充因子选项是为了对性能进行微调。但是，使用 sp_configure 系统存储过程指定的服务器范围的默认填充因子，在大多数情况下都是最佳的选择。

说明 即使对于一个面向许多插入和更新操作的应用程序来说，数据库读取次数一般也超过数据库写入次数的 5 到 10 倍。因此，指定一个不同于默认设置的填充因子会降低数据库的读取性能，而降低量与填充因子设置值成反比。例如，当填充因子的值为 50% 时，数据库的读取性能会降低两倍。

只有当在表中根据现有数据创建新索引，并且可以精确预见将来会对这些数据进行哪些更改时，将填充因子选项设置为另一个值才有用。

填充因子只在创建索引时执行；索引创建后，当表中进行数据的添加、删除或更新时，不会保持填充因子。如果试图在数据页上保持额外的空间，则将有背于使用填充因子的本意，因为随着数据的输入，SQL Server 必须在每个页上进行页拆分，以保持填充因子指定的空闲空间百分比。因此，如果表中的数据进行了较大的变动，添加了新数据，可以填充数据页的空闲空间。在这种情况下，可以重新创建索引，重新指定填充因子，以重新分布数据。

请参见

创建索引

fill factor 选项

表和索引构架


索引优化向导

索引优化向导使您得以为 Microsoft®SQL Server™ 2000 数据库选择和创建优化的索引集和统计信息集，而不需要深入了解 SQL Server 的数据库结构、工作负荷或内部原理。

为了生成适用的优化索引集建议，该向导需要工作负荷。工作负荷包含一个保存在文件或表中的 SQL 脚本或 SQL 事件探查器跟踪，其中有 SQL 批处理或远程过程调用 (RPC) 事件类以及 Event Class 和 Text 数据列。有关更多信息，请参见 TSQL 事件分类。

如果没有现有的工作负荷供索引优化向导进行分析，可以使用 SQL 事件探查器创建一个。可以使用 Sample 1 - TSQL 跟踪定义创建工作负荷，也可以创建一个新跟踪，用来捕获默认事件和数据列。当确定跟踪已捕获了正常数据库活动的一个具有代表性的样本时，该向导即可分析工作负荷，推荐使用将会提高数据库性能的索引配置。

索引优化向导可以：

  • 根据给定的工作负荷，通过使用查询优化器分析该工作负荷中的查询，为数据库推荐最佳索引组合。

  • 分析所建议的更改将会产生的影响，包括索引的使用，查询在表之间的分布，以及查询在工作负荷中的性能。

  • 推荐为执行一个小型的问题查询集而对数据库进行优化的方法。

  • 允许通过指定磁盘空间约束等高级选项对推荐进行自定义。

建议由 SQL 语句构成，执行这些语句可以创建更有效的新索引，如果需要，还可以除去无效的现有索引。建议在支持索引视图的平台上使用索引视图。索引优化向导提出建议后，该建议可以：

  • 立即执行。

  • 安排在以后通过创建执行 SQL 脚本的 SQL Server 作业执行。

  • 保存到 SQL 脚本中，用户在将来某个时候或在另一个服务器上手工执行。
注意事项

对于下列项目，索引优化向导不推荐索引：

  • 当前选定的数据库中不存在的跨数据库查询所引用的表。

  • 系统表。

  • PRIMARY KEY 约束和唯一索引。

其它索引优化向导注意事项包括：

  • 索引优化向导在一个工作负荷中最多可以包含 32,767 个可优化查询。负荷中超出此数目的查询将不被考虑。此外，带有被引用的标识符的查询将不予优化。

  • 索引优化向导通过数据采样搜集统计信息。因此，如果连续在同一个工作负荷上执行该向导，则每次推荐的索引可能各不相同，执行推荐索引后的改进效果也可能各不相同。

  • 索引优化向导不能用于在 SQL Server 6.5 版或更早版本的数据库上选择或创建索引和统计信息。

  • 当把 SQL 脚本保存到可用空间不足的磁盘上时，索引优化向导并不给出错误。

  • 在分析过程中，索引优化向导会消耗相当多的 CPU 及内存资源。最好在生产服务器的测试版上执行优化，而不要在生产服务器上执行。此外，最好在另一台计算机上而非运行 SQL Server 实例的计算机上运行该向导。

在下列情况下，索引优化向导可能不会提供索引建议：

  • 被采样的表中数据不足。

  • 建议的索引对现有索引的查询性能预计带来的改进不够充分。

工作负荷中的查询在唤醒调用索引优化向导的用户的安全上下文中进行分析。该用户必须是固定服务器角色 sysadmin 的成员。

为减少索引优化向导的执行时间：

  • 确保在"选择服务器和数据库"对话框中未选择"进行彻底分析"。进行彻底分析将会使索引优化向导对查询进行详尽的分析，导致执行时间延长。但选择此选项可以使所优化的工作负荷的性能得到更大的全面提高。

  • 只对数据库中表的子集进行优化。

  • 减小工作负荷文件的尺寸。

如果选定了"保留所有现有索引"选项，则索引优化向导不建议除去任何索引，而是在适当的时候只建议新索引。如果清除此选项，则工作负荷的性能会全面提高。此外，索引优化向导也不会推荐除去对 PRIMARY KEY 约束的索引或 UNIQUE 索引。但可能会除去或替换不是唯一或不是当前在 PRIMARY KEY 约束上创建的索引。

索引优化向导在最终建议内包括所有索引提示或查询提示，即使索引对于表并非是最佳的。可能会提议和建议查询中引用的其它表上的索引；然而，所有指定为提示的索引始终是最终建议的一部分。但提示会妨碍索引优化向导选择更好的执行计划。因此在分析工作负荷之前应考虑从查询中删除所有索引提示。

在 SQL 查询分析器中使用索引优化向导

SQL 查询分析器中的索引分析使得可以对单个查询或成批查询进行分析，并为用于支持给定查询或成批查询的优化索引集生成建议。只有 sysadmin 固定服务器角色成员才可以使用 SQL 查询分析器进行索引分析。

若要推迟生成索引优化向导所建议的索引，可使用 SQL 查询分析器保存建议的 SQL 脚本。将 SQL 脚本保存到文件中，可以使索引分析推荐的 Transact-SQL 语句在执行前得到检查，从而可以在执行前对 SQL 脚本进行编辑（例如，可以更改所生成的索引的名称）。

启动索引优化向导

企业管理器

企业管理器

使用索引优化向导分析查询

企业管理器

企业管理器

请参见

创建并管理跟踪和模板

使用 SQL 事件探查器进行监视


创建索引

确定了索引设计后，便可以在数据库的表上创建索引。

Microsoft® SQL Server™ 2000 自动创建唯一索引，以强制实施 PRIMARY KEY 和 UNIQUE 约束的唯一性要求。除非表中已存在聚集索引，或者显式指定了非聚集索引，否则将会创建一个唯一的聚集索引，以实施 PRIMARY KEY 约束。除非显式指定了聚集索引，否则，默认情况下创建唯一的非聚集索引以强制 UNIQUE 约束。

如果需要创建不依赖于约束的索引，可以使用 CREATE INDEX 语句。默认情况下，如果未指定聚集选项，将创建非聚集索引。

创建索引时须考虑的其它事项包括：

  • 只有表的所有者可以在同一个表中创建索引。

  • 每个表中只能创建一个聚集索引。

  • 每个表可以创建的非聚集索引最多为 249 个（包括 PRIMARY KEY 或 UNIQUE 约束创建的任何索引）。

  • 包含索引的所有长度固定列的最大大小为 900 字节。例如，不可以在定义为 char(300)、char(300) 和 char (301) 的三个列上创建单个索引，因为总宽度超过了 900 字节。

  • 包含同一索引的列的最大数目为 16。

在使用 CREATE INDEX 语句创建索引时，必须指定索引、表以及索引所应用的列的名称。作为 PRIMARY KEY 或 UNIQUE 约束的一部分或使用 SQL Server 企业管理器创建的新索引，会根据数据库表的名称，自动获得系统定义的名称。如果在一个表上创建多个索引，这些索引的名称被追加 _1、_2 等。必要时可对索引重新命名。

说明 当前数据库正在备份时不能在其上创建索引。

如果在具有多个辅助索引的表上创建聚集索引，则必须重建所有辅助索引，使它们包含聚集键值而非行标识符 (RID)。同样，如果在具有多个非聚集索引的表上删除聚集索引，所有非聚集索引将作为 DROP 操作的一部分重建。这在大型表上会花很长时间。

在大型表上生成索引的首选方法是以聚集索引开始，然后生成非聚集索引。除去所有索引时，首先除去非聚集索引，最后除去聚集索引。这样，就无需重建索引。

聚集索引

在创建聚集索引时，将会对表进行复制，对表中的数据进行排序，然后删除原始的表。因此，数据库上必须有足够的空闲空间，以容纳数据复本。

默认情况下，表中的数据在创建索引时排序。但是，如果因聚集索引已经存在，且正在使用同一名称和列重新创建，而数据已经排序，则会重建索引，而不是从头创建该索引，以自动跳过排序操作。重建操作会检查行是否在生成索引时进行了排序。如果有任何行排序不正确，即会取消操作，不创建索引。

唯一索引

创建唯一索引可以确保任何生成重复键值的尝试都会失败。如果创建的单个查询导致添加了重复的和非重复的键值，SQL Server 会拒绝所有的行，包括非重复的键值。例如，如果一个单个的插入语句从表 A 检索了 20 行，然后将它们插入到表 B 中，而这些行中有 10 行包含重复键值，则默认情况下所有 20 行都将被拒绝。不过，在创建该索引时可以指定 IGNORE_DUP_KEY 子句，使得只有重复的键值才被拒绝，而非重复的键值将被添加。在上面的例子中，将只会拒绝 10 个重复的键值，其它 10 个非重复的键值将被添加到表 B 中。

如果有任何重复的键值，便不能创建唯一索引。例如，如果要在 a 列和 b 列上创建唯一的组合索引，而表中有两行的 a 列同为值 1，b 列同为值 2，则无法创建唯一索引。

说明 如果一个单个的列中有不止一行包含 NULL，则无法在该列上创建唯一索引。同样，如果列的组合中有多行包含 NULL 值，则不能在多个列上创建唯一索引。在创建索引时，这些被视为重复的值。

在创建表时创建索引

Transact-SQL

企业管理器

SQL-DMO

在现有表上创建索引

Transact-SQL

企业管理器

SQL-DMO

也可以使用 SQL Server 企业管理器中的创建索引向导创建索引。

用创建索引向导创建索引

企业管理器

企业管理器

请参见

全文索引

DBCC SHOWCONTIG

填充因子

index create memory 选项

在文件组上放置索引

PRIMARY KEY 约束

UNIQUE 约束


在计算列上创建索引

只要满足下列要求，则可在计算列上定义索引：

  • computed_column_expression 必须是确定性的。如果表达式对一组给定的输入总是返回同样的结果，则该表达式是确定性的。在下列情况中，computed_column_expression 是确定性的：
    • 表达式引用的所有函数都是确定和精确的。这包括用户定义的函数和内置函数。有关更多信息，请参见确定性函数和非确定性函数。

    • 表达式引用的所有列都来自包含计算列的表。

    • 没有列引用从多行拉数据。例如，聚合函数（如 SUM 或 AVG）依靠来自多行的数据，这使得 computed_column_expression 不确定。

    COLUMNPROPERTY 函数的 IsDeterministic 属性报告 computed_column_expression 是否确定。

    计算列表达式在下列情况中精确：

    • 非 float 数据类型表达式。

    • 其定义中不使用 float 数据类型。例如，在下列语句中，列 y 为 int 型并且确定，但不精确：
      CREATE TABLE t2 (a int, b int, c int, x float,
    y AS CASE x
            WHEN 0 THEN a
            WHEN 1 THEN b
            ELSE c
        END)

    COLUMNPROPERTY 函数的 IsPrecise 属性报告 computed_column_expression 是否精确。

    说明 任何 float 型表达式都被认为是不精确的，不能作为索引键；float 型表达式可以用在索引视图中，但不能作为键。对于计算列同样如此。如果函数、表达式、用户定义的函数或视图定义中包含任何 float 表达式（包括逻辑表达式（比较）），则被认为是不确定的。

  • 执行 CREATE TABLE 语句时，必须将 ANSI_NULL 连接级选项设置为 ON。OBJECTPROPERTY 函数通过 IsAnsiNullsOn 属性报告此选项是否已打开。

  • 为计算列定义的 computed_column_expression 不能取值为 text、ntext 或 image 数据类型。

  • 对于创建索引的连接和所有尝试使用 INSERT、UPDATE 或 DELETE 语句更改索引值的连接，必须使六个 SET 选项设置为 ON，一个选项设置为 OFF。如果不具有上述选项设置的连接执行了任何 SELECT 语句，优化器将忽略计算列上的索引。

    下列选项必须设置为 ON：

    • ANSI_NULLS

    • ANSI_PADDING

    • ANSI_WARNINGS

    • ARITHABORT

    • CONCAT_NULL_YIELDS_NULL

    • QUOTED_IDENTIFIER

    除上述 ON 设置外，NUMERIC_ROUNDABORT 选项必须设置为 OFF。有关更多信息，请参见影响结果的 SET 选项。


在视图上创建索引

可以在视图上定义索引。索引视图是一种在数据库中存储视图结果集的方法，可减少动态生成结果集的开销。索引视图还能自动反映出创建索引后对基表数据所做的修改。

说明 只有安装了 Microsoft® SQL Server™ 2000 企业版或 Microsoft SQL Server 2000 开发版，才可以创建索引视图。

索引视图具有以下优点：

  • 通过对 CREATE INDEX 和 CREATE VIEW 语句进行简单语法扩展即可实现索引视图。

  • 索引视图中的数据随基表数据的更新自动更新，其方式与基表索引键的自动更新方式大体相同。无须使索引视图的内容与基础基表中的数据同步。

  • 无须在查询中指定特殊的提示，SQL Server 优化器就会考虑索引视图。即使查询未在 FROM 子句中直接引用索引视图，优化器也将通过尝试匹配为视图生成的查询计划和为查询生成的计划的某些部分来考虑索引视图。

  • 若要将索引视图引入现有的数据库，必须只发出相关的 CREATE VIEW 和 CREATE INDEX 语句。必须对应用程序代码做少许改动才能使 SQL Server 利用视图上的任何索引。

索引优化向导除建议在基表上使用索引外，还建议使用索引视图。使用该向导能极大地提高管理员确定组合使用索引与索引视图的能力，以便优化在数据库上执行的典型查询混合的性能。

与基于基表的索引相比，维护索引视图更为复杂。只有当视图的结果检索速度的收益超过了修改所需的开销时，才应在视图上创建索引。

请参见

设计索引视图

创建索引视图

在视图上使用索引


影响结果的 SET 选项

计算列上的索引视图和索引涉及在数据库中存储结果供日后引用。只有当所有引用结果的连接会生成与创建存储结果集的连接相同的结果集时，这些存储结果才有效。

索引视图

索引视图通过在视图上创建聚集索引来存储视图返回的结果集。对于复杂视图，存储结果集能极大加快数据检索速度。只有当所有引用视图的操作使用与生成结果操作完全相同的算法时，索引视图才有用。与计算列的索引一样，创建索引视图的算法也包括：

  • 首先生成结果集的 CREATE INDEX 语句。

  • 任何影响用于生成视图结果集的基础数据的后续 INSERT、UPDATE 或 ELETE 语句。

  • 优化器必须为其确定使用索引视图是否有效的所有查询。
计算列上的索引

计算列上的索引必须计算计算列的值以生成存储于索引中的键。只有当使用索引的所有操作使用与确定键值操作完全相同的算法时，计算列上的索引才有效：

  • 建立第一个键值集的原始 CREATE INDEX 语句。

  • 当后续的 INSERT、UPDATE 和 DELETE 语句创建、改变或删除键值时，这些操作将不再有效，除非使用与最初创建索引操作相同的算法进行计算时才有效。

  • 若要使索引对任何后续语句都有用，存储在索引中的所有键值必须与将由执行此语句的连接的当前设置生成的键值相同。
SET 选项设置

任何对 Transact-SQL 语句生成结果有影响的 SET 选项对所有引用索引的操作都必须具有相同的设置。有七个 SET 选项影响计算列中的存储结果和视图返回的结果。所有使用索引视图或计算列上的索引的连接对这七个选项必须具有相同的设置：

  • 下列六个 SET 选项必须设置为 ON：
    • ANSI_NULLS

    • ANSI_PADDING

    • ANSI_WARNINGS

    • ARITHABORT

    • CONCAT_NULL_YIELDS_NULL

    • QUOTED_IDENTIFIER
  • 必须将选项 NUMERIC_ROUNDABORT 设置为 OFF。

必须为在视图或计算列上创建索引的所有连接正确设置上述 SET 选项。任何执行 INSERT、UPDATE 或 DELETE 语句更改索引中存储的数据值的连接都必须具有正确的设置。这包括大容量复制、数据转换服务 (DTS) 和复制操作。对于不具有正确选项设置的连接所尝试的插入、更新或删除操作，Microsoft® SQL Server™ 2000 将生成一个错误并回滚这些操作。如果连接不具有正确的选项设置，则优化器不考虑在任何 Transact-SQL 语句的执行计划中使用计算列索引或视图索引。

例如，使用以下脚本定义并填充表：

CREATE TABLE Parts
  (PartID            int PRIMARY KEY,
   PartName          char(10),
   PartMaterial      char(10),
   PartColor         char(10),
   PartDescription   AS PartMaterial + PartColor
  )
GO
INSERT INTO Parts VALUES (1, 'Table', 'Wood', 'Red')
INSERT INTO Parts VALUES (2, 'Chair', 'Fabric', 'Blue')
INSERT INTO Parts VALUES (3, 'Bolt', 'Steel', NULL)
GO

对于 PartID 为 3的行，PartDescription 列的计算值取决于选项 CONCAT_NULL_YIELDS_NULL。如果 CONCAT_NULL_YIELDS_NULL 设置为 ON，则计算值为 NULL。如果 CONCAT_NULL_YIELDS_NULL 设置为 OFF，则计算值为字符串 'Steel'。若要正确维护 PartDescription 列上的索引，所有 INSERT、UPDATE 和 DELETE 选项都必须具有与创建索引的连接相同的 CONCAT_NULL_YIELDS_NULL ON 设置。优化器也不对 CONCAT_NULL_YIELDS_NULL 设置与创建键值的连接不同的连接使用索引。

OLE DB 和 ODBC 连接的 SET 选项设置

计算列索引和视图上的索引要求的七个 SET 选项设置中，有六个是用于 SQL Server 的 OLE DB 提供程序和 SQL Server ODBC 驱动程序的默认设置。这些设置是：

  • ANSI_NULLS

  • ANSI_PADDING

  • ANSI_WARNINGS

  • CONCAT_NULL_YIELDS_NULL

  • NUMERIC_ROUNDABORT

  • QUOTED_IDENTIFIER

这些设置也执行 SQL-92 标准的规则并且是用于 SQL Server 的推荐设置。因为 SQL Server 2000 中的 DTS、复制和大容量复制等操作使用 OLE DB 或 ODBC，所以也自动为这些操作设置上述选项。有些 SQL Server 实用工具将一个或多个 ANSI 设置设为 OFF 以保持与实用工具的早期版本兼容。

SET ARITHABORT ON 是一个不自动为使用用于 SQL Server 的 OLE DB 提供程序和 SQL Server ODBC 驱动程序的连接设置的选项。OLE DB 和 ODBC 连接不指定 ARITHABORT 设置，所以连接默认为服务器的默认值，即 ARITHABORT OFF。该服务器默认值由 user options 服务器选项控制。对于在视图或计算列上实现索引的任何服务器，应设置此相当于 64 user options 位。有关如何设置该选项的更多信息，请参见 user options 选项。

设置选项的优先顺序

可以在几种级别上指定 SET 选项的设置。特定连接的每个会话选项的最终设置由设置该选项的最高优先权操作确定。会话设置操作的优先权为（具有最高优先权的列在最前面）：

  • 任何应用程序都可以通过在连接到服务器后执行 SET 语句来显式替代任何默认设置。SET 语句替代所有原来的设置并可用于在应用程序执行时动态地打开或关闭选项。

  • OLE DB 和 ODBC 应用程序通过在连接字符串中指定选项设置，可以指定连接时生效的选项设置。

  • 通过使用"控制面板"中的 ODBC 应用程序或 ODBC SQLConfigDataSource 函数，可以将任何 SQL Server ODBC 数据源的 SET 选项设置为 ON 或 OFF。使用该数据源的 ODBC 应用程序所建立的任何连接都使用指定的默认值，除非应用程序在连接后使用 SET 语句替代连接字符串中的默认值。

  • 用于 SQL Server 的 OLE DB 提供程序和 SQL Server ODBC 驱动程序自动将七个会话选项设置为索引视图所要求的设置。DB-Library 和用于 C 语言的嵌入式 SQL 应用程序则不是这样，所以，使用这些 API 的系统必须对应用程序进行编码以发出正确的 SET 语句，或者将数据库或服务器的默认值更改为正确设置。

  • 可以使用 ALTER DATABASE 或 SQL Server 企业管理器为数据库建立默认设置。

  • 可以为服务器建立默认设置，方法是使用 sp_configure 或 SQL Server 企业管理器设置名为 user options 的服务器配置选项。有关更多信息，请参见 user options 选项。

在以下情况中，索引视图和计算列上的视图所要求的连接选项设置必须是活动的：

  • 对于在视图或计算列上创建索引的任何连接。

  • 对于任何试图修改视图或计算列上的索引所覆盖数据的 INSERT、UPDATE 或 DELETE 语句。

  • 在优化器考虑在视图或计算列上使用索引覆盖查询之前。

  • 对于索引视图，在创建视图时，ANSI_NULLS 和 QUOTED_IDENTIFIER 选项必须设置为 ON，因为这两个设置将作为对象属性与视图定义一起存储。
注意事项

SET 语句可以动态地改变选项；因此，在具有索引视图和计算列索引的数据库上发出 SET 语句时必须谨慎。例如，应用程序可以建立一个连接，使默认设置允许引用索引视图。如果该连接调用第一个语句为 SET ANSI_WARNINGS OFF 的存储过程，则该语句将替代 ANSI_WARNINGS 原先的默认值或设置。在处理存储过程中的任何语句时，优化器忽略所有索引视图或计算列上的索引。存储过程中的任何语句若试图进行 INSERT、UPDATE 或 DELETE 操作，都会影响索引视图或计算列上的索引，并由此产生错误。

一些最初在 SQL Server 早期版本上开发的存储过程或触发器中的逻辑取决于一些选项设置为 OFF，如 QUOTED_IDENTIFIER 或 ANSI_NULLS。同样，默认情况下，DB-Library 和用于 C 语言的嵌入式 SQL 应用程序不设置任何会话选项。对于其它依赖设置为 ON 的选项的存储过程或触发器，来自这些应用程序的连接会引起问题。建议的解决方法是对此类存储过程和触发器的起始 SET 语句进行编码，以确保具备所需的操作环境。在 SQL Server 2000 中，如果存储过程或触发器为视图和计算列上的索引所需的选项设置的值与索引所要求的不同，则不能使用索引覆盖任何由存储过程或触发器执行的 SELECT 语句。如果由这些存储过程和触发器执行的 INSERT、UPDATE 或 DELETE 语句修改视图和计算列上的索引所覆盖的数据，则这些语句都会失败。在使用索引视图和计算列索引的 SQL Server 2000 实例中，所编写的存储过程和触发器应能够与支持这些索引所需的七个 SET 选项一起使用。如果系统接收来自使用 DB-Library、用于 C 语言的嵌入式 SQL 或 SQL Server 6.5 版或更早版本的 ODBC 驱动程序的客户端的连接，则 SET 语句应只用在这些系统的存储过程和触发器中。存储过程和触发器应只将这些选项设置为索引视图和计算列索引所要求的那样。

以下三个会话选项可以潜在地影响结果集的格式：DATEFIRST、DATEFORMAT 和 LANGUAGE。结果受这些选项的改动影响的任何函数称为不确定函数，不确定函数不能用在已建立索引的视图或计算列中。

请参见

CREATE INDEX

分布式查询

SET ANSI_NULLS

SET ANSI_PADDING

SET ANSI_WARNINGS

SET ARITHABORT

SET CONCAT_NULL_YIELDS_NULL

SET NUMERIC_ROUNDABORT

SET QUOTED_IDENTIFIER


创建升序和降序索引

定义索引时，可以指定每列的数据是按升序还是降序存储。如果不指定，则默认为升序，保持与 Microsoft® SQL Server™ 2000 早期版本兼容。

CREATE TABLE、CREATE INDEX 和 ALTER TABLE 语句的语法在索引中的各列上支持关键字 ASC（升序）和 DESC（降序）：

CREATE TABLE ObjTable
   (ObjID      int PRIMARY KEY,
    ObjName      char(10),
    ObjWeight   decimal(9,3)
    )
CREATE NONCLUSTERED INDEX DescIdx ON
         ObjTable(ObjName ASC, ObjWeight DESC)

INDEXKEY_PROPERTY 元数据函数报告索引列是按升序还是降序存储。另外，sp_helpindex 和 sp_helpconstraint 系统存储过程报告索引键列的方向。降序索引列将在结果集中列出，在其名称后带有减号 (-)。默认值（升序索引列）将仅按名称列出。

在大多数引用表的查询包含指定不同键列存储方向的 ORDER BY 子句时，指定键值存储顺序的能力非常有用。例如，先前为 ObjTable 定义的索引可以完全消除对 ORDER BY 子句的需要，如：

ORDER BY ObjName ASC, ObjWeight DESC

SQL Server 的内部算法在单列索引的两个不同方向上的效果是等同的，与键的存储顺序无关。例如，在单列索引上指定 DESC 并不能使带有 ORDER BY IndexKeyCol DESC 子句的查询比为索引指定了 ASC 的情况运行得更快。


统计信息

Microsoft® SQL Server™ 2000 允许创建有关值在列中的分布情况的统计信息。该统计信息可供查询处理器用来确定最佳的查询评估策略。在创建索引时，SQL Server 会自动存储有关值在索引列中的分布情况的统计信息。SQL Server 中的查询优化器使用这些统计信息估算使用该索引进行查询的成本。另外，当 AUTO_CREATE_STATISTICS 数据库选项设置为 ON（默认值）时，SQL Server 自动为没有列索引用于谓词的列创建统计信息。

随着列中数据的更改，索引和列的统计信息可能会过时，导致查询优化器选择的查询处理方法不是最佳的。例如，如果创建一个包含一个索引列和 1,000 行数据的表，每一行在索引列中的值都是唯一的，则查询优化器将把该索引列视为搜集查询数据的好方法。如果更新列中的数据，因而有很多重复值，则该列对于查询不再是理想的候选列。然而，查询优化器仍然根据索引的过时分发统计信息（这些信息基于的是更新前的数据），将该列看作是一个好的候选列。

说明 在使用 SQL 查询分析器以图形方式显示查询的执行计划时，过时或丢失的统计信息将予以警告显示（表名用红色文字显示）。有关更多信息，请参见使用 SQL 查询分析器以图形方式显示执行计划。另外，使用 SQL 事件探查器监视 Missing Column Statistics 事件类可以指示何时缺少统计信息。有关更多信息，请参见错误和警告事件分类。

因此，随着表中的数据发生变化，SQL Server 会自动定期更新这些统计信息。采样是在各个数据页上随机进行的，取自表或统计信息所需的列上的最小非聚集索引。从磁盘读取一个数据页后，该数据页上的所有行都被用来更新统计信息。统计信息更新的频率取决于列或索引中的数据量以及数据更改量。例如，对于一个包含 10,000 行的表，当 1,000 个索引值发生更改时，该表的统计信息便可能需要更新，因为 1,000 个值可能在该表中占有很大的比例。而对于包含 10，000，000 个索引条目的表来说，有 1,000 个索引值发生更改的意义要小得多，因此统计信息便可能不会自动更新。但是，SQL Server 总是确保至少对若干行进行采样；对小于 8 兆字节 (MB) 的表搜集统计信息时，总是进行完全扫描。

通过进行数据采样而不是分析所有数据，可以极大地减少这种统计信息的自动更新所带来的成本。在某些情况下，统计采样无法获得表中数据的精确特征。可以使用 UPDATE STATISTICS 语句的 SAMPLE 和 FULLSCAN 子句，控制在逐个表手工更新统计信息时采样的数据量。FULLSCAN 子句指定搜集信息时扫描表中所有数据，而 SAMPLE 子句可用来指定采样的行数百分比或采样的行数。

也可以用下列方式告知 SQL Server 不必维护给定列或索引上的统计信息：

  • 使用系统存储过程 sp_autostats。

  • 使用 CREATE INDEX 语句的 STATISTICS_NORECOMPUTE 子句。

  • 使用 UPDATE STATISTICS 语句的 NORECOMPUTE 子句。

  • 使用 CREATE STATISTICS 语句的 NORECOMPUTE 子句。

  • 使用 ALTER DATABASE 语句将 AUTO_CREATE_STATISTICS 和 AUTO_UPDATE_STATISTICS 数据库选项设置为 OFF。有关更多信息，请参见设置数据库选项。

如果通知 SQL Server 不要自动保留统计信息，则需要手工更新统计信息。

还可以使用系统存储过程 sp_createstats 以一个语句对当前数据库中所有用户表的适于统计的列创建统计信息。不适于统计的列包括不确定或不精确计算列，或数据类型为 image、text 和 ntext 的列。

如果不再需要保留和维护为一个列生成的统计信息，可以将这些信息删除。SQL Server（当数据库选项设置为 ON 时）在列上创建的统计信息将自动老化和除去。

手动创建统计信息使得可以创建包含多种列密度（列组合的重复值的平均数量）的统计信息。例如，一个查询包含如下子句：

WHERE a = 7 and b = 9

在这两个列 (a, b) 上一起手动创建统计信息使 SQL Server 可以对查询作出更准确的估计，因为该统计信息也包含 a 和 b 列组合的不重复值的平均数量。

在列上创建统计信息

Transact-SQL

查询分析器

查询分析器

SQL-DMO

在所有用户表的所有适于统计的列上创建统计信息

Transact-SQL

手工更新统计信息

Transact-SQL

查询分析器

查询分析器

SQL-DMO

查看表的统计信息

Transact-SQL

SQL-DMO

删除列的统计信息

Transact-SQL

查询分析器

查询分析器

SQL-DMO

请参见

CREATE INDEX

分发内容统计

sp_autostats

sp_dboption


重建索引

在数据库中创建索引时，查询所使用的索引信息存储在索引页中。连续索引页由从一个页到下一个页的指针链接在一起。当对数据的更改影响到索引时，索引中的信息可能会在数据库中分散开来。重建索引可以重新组织索引数据（对于聚集索引还包括表数据)的存储，清除碎片。这可通过减少获得请求数据所需的页读取数来提高磁盘性能。

在 Microsoft® SQL Server™ 2000 中，如果要用一个步骤重新创建索引，而不想删除旧索引并重新创建同一索引，则使用 CREATE INDEX 语句的 DROP_EXISTING 子句可以提高效率。这一优点既适用于聚集索引也适用于非聚集索引。

以删除旧索引然后重新创建同一索引的方式重建聚集索引，是一种昂贵的方法，因为所有二级索引都使用聚集键指向数据行。如果只是删除聚集索引然后重新创建，则会使所有非聚集索引都被删除和重新创建两次。一旦删除聚集索引并再次重建该索引，就会发生这种情形。通过在一个步骤中重新创建索引，可以避免这一昂贵的做法。在一个步骤中重新创建索引时，会告诉 SQL Server 要重新组织现有索引，避免了删除和重新创建非聚集索引这些不必要的工作。该方法的另一个重要优点是可以使用现有索引中的数据排序次序，从而避免了对数据重新排序。这对于聚集索引和非聚集索引都十分有用，可以显著减少重建索引的成本。另外，通过使用 DBCC DBREINDEX 语句，SQL Server 还允许对一个表重建（在一个步骤中）一个或多个索引，而不必单独重建每个索引。

DBCC DBREINDEX 也可用于重建执行 PRIMARY KEY 或 UNIQUE 约束的索引，而不必删除并创建这些约束（因为对于为执行 PRIMARY KEY 或 UNIQUE 约束而创建的索引，必须先删除该约束，然后才能删除该索引）。例如，可能需要在 PRIMARY KEY 约束上重建一个索引，以便为该索引重建给定的填充因子。

删除索引

Transact-SQL

企业管理器

SQL-DMO

在现有表中创建索引

Transact-SQL

企业管理器

SQL-DMO

用一个步骤重新创建索引

Transact-SQL

SQL-DMO

在表中重建一个或多个索引

Transact-SQL

修改索引

企业管理器

请参见

创建索引

删除索引


重命名索引

只要索引名在表中是唯一的，就可对索引重命名。例如，两个表可以有一个名为 XPK_1 的索引，但同一个表不能有两个名为 XPK_1 的索引。对索引重命名只会更改索引的名称，而不会重建索引。

说明 当在表中创建 PRIMARY KEY 或 UNIQUE 约束时，会在表中自动创建一个与该约束同名的索引。由于索引名对于表必须是唯一的，所以无法再通过创建或重命名获得一个与该表的 PRIMARY KEY 或 UNIQUE 约束同名的索引。

重命名索引

Transact-SQL

企业管理器

SQL-DMO

修改索引

企业管理器

请参见

创建索引


查看索引

在表上创建索引或者 PRIMARY KEY 或 UNIQUE 约束之后，可能需要查找有关索引的信息。例如，可能需要查明索引类型，以及那些是某个表的索引的列，或者某个索引使用的数据库空间总量。

每个为全文索引进行了注册的表，都有一个索引被选作全文键。可以查看索引的属性，以确定该索引是不是全文键。

查看表中的索引

Transact-SQL

企业管理器

SQL-DMO

SQL Server 企业管理器可以显示数据库中所有索引的名称，以及这些索引所属的表的名称。

如何查看数据库中的全部索引

企业管理器

企业管理器

查看索引使用的空间

Transact-SQL

SQL-DMO

查看索引属性

Transact-SQL

SQL-DMO

请参见

创建索引

全文索引


删除索引

当一个索引不再需要时，可以将其从数据库中删除，以回收它当前使用的存储空间。这些回收的空间可以由数据库中的任何对象使用。

删除聚集索引可能要花费一些时间，因为必须重建同一个表上的所有非聚集索引。有关聚集索引与非聚集索引之间的关系，请参见非聚集索引。

必须先删除约束后，才能删除 PRIMARY KEY 或 UNIQUE 约束使用的索引。若要在不删除和重新创建 PRIMARY KEY 或 UNIQUE 约束的情况下，删除并重新创建该约束使用的索引（例如，重新实施该索引使用的原填充因子），应通过一个步骤重建该索引。有关重建索引的更多信息，请参见重建索引。不能删除指定为表的全文键的索引。应查看索引属性以确定是不是全文键。

重建索引而不是删除之后再重新创建，这种方法也可以用于重新创建聚集索引，因为在重建索引的过程中，如果数据已经处于有序状态，可以不必按索引列再对数据排序。

删除视图或表时，自动删除在视图或表（永久性和临时性）上创建的索引。

说明 只有表的所有者可以删除其索引。所有者无法将该权限转让给其他用户。

删除索引

Transact-SQL

企业管理器

SQL-DMO

查看索引属性

Transact-SQL

SQL-DMO

请参见

创建索引


视图

视图是一个虚拟表，其内容由查询定义。同真实的表一样，视图包含一系列带有名称的列和行数据。但是，视图并不在数据库中以存储的数据值集形式存在。行和列数据来自由定义视图的查询所引用的表，并且在引用视图时动态生成。

对其中所引用的基础表来说，视图的作用类似于筛选。定义视图的筛选可以来自当前或其它数据库的一个或多个表，或者其它视图。分布式查询也可用于定义使用多个异类源数据的视图。如果有几台不同的服务器分别存储组织中不同地区的数据，而您需要将这些服务器上相似结构的数据组合起来，这种方式就很有用。

通过视图进行查询没有任何限制，通过它们进行数据修改时的限制也很少。

下图显示了在两个表上建立的视图。

请参见

SQL 视图


视图使用方案

视图通常用来集中、简化和自定义每个用户对数据库的不同认识。视图可用作安全机制，方法是允许用户通过视图访问数据，而不授予用户直接访问视图基础表的权限。从（或向）Microsoft® SQL Server™ 2000 复制数据时也可使用视图来提高性能并分区数据。

着重于特定数据

视图让用户能够着重于他们所感兴趣的特定数据和所负责的特定任务。不必要的数据可以不出现在视图中。这同时增强了数据的安全性，因为用户只能看到视图中所定义的数据，而不是基础表中的数据。有关出于安全考虑使用视图的更多信息，请参见使用视图作为安全机制。

简化数据操作

视图可以简化用户操作数据的方式。可将经常使用的联接、投影、联合查询和选择查询定义为视图，这样，用户每次对特定的数据执行进一步操作时，不必指定所有条件和限定。例如，一个用于报表目的，并执行子查询、外联接及聚合以从一组表中检索数据的复合查询，就可以创建为一个视图。视图简化了对数据的访问，因为每次生成报表时无须写或提交基础查询，而是查询视图。有关数据操作的更多信息，请参见查询基础知识。

还可以创建内嵌的用户定义函数，在逻辑上作为参数化视图运行，或作为在 WHERE 子句搜索条件中含有参数的视图运行。有关更多信息，请参见内嵌用户定义函数。

自定义数据

视图允许用户以不同的方式查看数据，即使他们同时使用相同的数据时也如此。这在具有不同目的和技术水平的用户共享同一个数据库时尤为有利。例如，可定义一个视图以仅检索由客户经理处理的客户数据。视图可以根据使用该视图的客户经理的登录 ID 决定检索哪些数据。

导出和导入数据

可使用视图将数据导出至其它应用程序。例如，可能希望使用 pubs 数据库中的 stores 和 sales 表在 Microsoft® Excel 中分析销售数据。为此，可创建一个基于 stores 和 sales 表的视图。然后使用 bcp 实用工具导出由视图定义的数据。也可使用 bcp 实用工具或 BULK INSERT 语句将数据从数据文件中导入某些视图，条件是可以使用 INSERT 语句在视图中插入行。有关向视图中复制数据的限制的更多信息，请参见 INSERT。有关使用 bcp 实用工具和 BULK INSERT 语句从（或向）视图中复制数据的更多信息，请参见向（或从）视图中复制数据。

组合分区数据

Transact-SQL UNION 集合运算符可在视图内使用，以将来自不同表的两个或多个查询结果组合成单一的结果集。这在用户看来是一个单独的表，称为分区视图。例如，如果一个表含有华盛顿的销售数据，另一个表含有加利福尼亚的销售数据，即可从 UNION 创建这两个表的视图。该视图代表了这两个区域的销售数据。

使用分区视图时，首先创建几个相同的表，指定一个约束以决定可在各个表中添加的数据范围。视图即使用这些基表创建。当查询该视图时，SQL Server 自动决定查询所影响的表，并仅引用这些表。例如，如果一个查询指定只需要华盛顿州的销售数据，则 SQL Server 只读取含有华盛顿销售数据的表，而并不访问其余的表。

分区试图可基于来自多个异类源——如远程服务器——的数据，而不仅仅局限于同一数据库中的表。例如，要将分别存储组织中不同区域数据的几台远程服务器上的数据组合起来，可以创建分布式查询，从每个数据源中检索数据，然后基于这些分布式查询创建视图。所有查询都只从包含查询所请求数据的远程服务器上读取表中的数据，其它在视图中由分布式查询引用的服务器均不被访问。

当在多个表或服务器上分割数据时，因为需扫描的数据较少，所以只访问一部分数据的查询运行得较快。如果表位于不同的服务器或一台多处理器的计算机上，同样可以对查询所涉及的表进行同时扫描，从而改进查询的性能。此外，象索引重建或表备份这样的维护任务可更快地执行。

通过使用分区视图，数据的外观象是一个单一表，且能以单一表的方式进行查询，而无须手动引用真正的基础表。

如果满足下列任一条件，则分区视图可被更新：

  • 在带有逻辑的视图上定义支持 INSERT、UPDATE 和 DELETE 语句的 INSTEAD OF 触发器。

  • 视图和 INSERT、UPDATE 及 DELETE 语句均遵从为可更新分区视图定义的规则。有关更多信息，请参见创建分区视图。

请参见

联接基础知识

使用分区视图


创建视图

在创建视图前请考虑如下原则：

  • 只能在当前数据库中创建视图。但是，如果使用分布式查询定义视图，则新视图所引用的表和视图可以存在于其它数据库中，甚至其它服务器上。

  • 视图名称必须遵循标识符的规则，且对每个用户必须为唯一。此外，该名称不得与该用户拥有的任何表的名称相同。

  • 可以在其它视图和引用视图的过程之上建立视图。Microsoft® SQL Server™ 2000 允许嵌套多达 32 级视图。

  • 不能将规则或 DEFAULT 定义与视图相关联。

  • 不能将 AFTER 触发器与视图相关联，只有 INSTEAD OF 触发器可以与之相关联。

  • 定义视图的查询不可以包含 ORDER BY、COMPUTE 或 COMPUTE BY 子句或 INTO 关键字。

  • 不能在视图上定义全文索引定义。

  • 不能创建临时视图，也不能在临时表上创建视图。

  • 不能除去参与到用 SCHEMABINDING 子句创建的视图中的表或视图，除非该视图已被除去或更改而不再具有架构绑定。另外，如果参与具有架构绑定的视图的表执行 ALTER TABLE 语句影响视图定义，则这些语句将失败。

  • 不能对视图执行全文查询，但是如果查询所引用的表被配置为支持全文索引，就可以在视图定义中包含全文查询。

  • 下列情况下必须在视图中指定每列的名称：
    • 视图中有任何从算术表达式、内置函数或常量派生出的列。

    • 视图中两列或多列具有相同名称（通常由于视图定义包含联接，而来自两个或多个不同表的列具有相同的名称）。

    • 希望使视图中的列名与它的源列名不同。（也可以在视图中重命名列。）无论重命名与否，视图列都会继承其源列的数据类型。

      说明 本规则在视图基于含有外部联接查询的情况下不适用，因为列可能从不支持空值转而支持空值。

      其它情况下，无须在创建视图时指定列名。SQL Server 使视图中的列与定义视图的查询所引用的列具有相同的名称和数据类型。选择列表可以是基表中列名的完整或部分列表。

若要创建视图，数据库所有者必须授予您创建视图的权限，并且您对视图定义中所引用的表或视图要有适当的权限。

默认情况下，由于行通过视图进行添加或更新，当其不再符合定义视图的查询的条件时，它们即从视图范围中消失。例如，可创建一个查询，从而定义一个视图以在表中检索所有员工薪水低于 $30,000 的行。如果该员工的薪水涨到了 $32,000，则查询视图时该特定员工将不再出现，因其薪水不符合视图所设的标准。但是，WITH CHECK OPTION 子句强制所有数据修改语句均根据视图执行，以符合定义视图的 SELECT 语句中所设的条件。如果使用该子句，修改行时需考虑到不让它在修改完后从视图中消失。任何可能导致行消失的修改都会被取消，并显示错误信息。

可对敏感性视图的定义进行加密，以确保不让任何人得到它的定义，包括视图的所有者。

创建视图

Transact-SQL

企业管理器

SQL-DMO

也可以使用 SQL Server 企业管理器的创建视图向导来创建视图。

用创建视图向导创建视图

企业管理器

企业管理器

请参见

通过视图修改数据

SQL 视图

视图解析

使用视图作为安全机制


设计索引视图

索引视图显著提高了某些类型查询的性能。

说明 只有安装了 Microsoft® SQL Server™ 2000 企业版或 Microsoft SQL Server 2000 开发版，才可以创建索引视图。

如果很少更新基础数据，则索引视图的效果最佳。索引视图的维护可能比维护表索引的成本高。如果经常更新基础数据，则维护索引视图数据的成本可能超过使用索引视图所带来的性能收益。

索引视图提高下列查询类型的性能：

  • 处理大量行的联接和聚合。

  • 许多查询经常执行的联接和聚合操作。

    例如，在联机事务处理 (OLTP) 数据库记录清单中，许多查询都希望联接 Parts、PartSupplier 和 Suppliers 表。虽然每个执行此联接的查询需要处理的行可能并不多，但若将成百上千这样的查询联接起来，则总的处理量将非常大。因为这些关系不大可能经常更新，可定义一个索引视图来存储联接结果，以提高整个系统的总体性能。

  • 决策支持工作负荷。

    分析系统的特点是存储不经常更新的、汇总的聚合数据。而许多决策支持查询的特点是进一步聚合数据并联接大量行。

索引视图通常不会提高下列查询类型的性能：

  • 具有大量写操作的 OLTP 系统。

  • 具有大量更新操作的数据库。

  • 不涉及聚合或联接的查询。

  • 具有键的高基数度的数据聚合。高基数度表示键包含许多不同的值。唯一键具有可能的最高基数度，因为每个键具有不同的值。索引视图通过减少查询必须访问的行数来提高性能。如果视图结果集的行数与基表的行数差不多，则使用视图获得的性能收益微乎其微。例如，请看以下对一个有 1,000 行的表的查询：
    SELECT PriKey, SUM(SalesCol)
FROM ExampleTable
GROUP BY PriKey

    如果表键的基数是 100，那么使用该查询结果生成的索引视图将只有 100 行。使用视图的查询所需的平均读取量是在基表上进行读取的十分之一。如果键是唯一键，则键的基数是 1000 且视图结果集返回 1000 行。这种使用索引视图而不是直接读取基表的查询方式，在性能方面没有任何收益。

  • 扩充联接，即结果集大于基表中原始数据的视图。
索引视图与查询相结合

虽然对可索引的视图的限制可能使您无法设计解决全部问题的视图，但仍可以设计多个较小的索引视图以加快部分进程的速度。

请考虑下列示例：

  • 假设某个经常执行的查询不仅要聚合某一数据库中的数据，还要聚合另一数据库中的数据，并且要联接结果。因为索引视图不能从多个数据库中引用表，所以不能设计单个视图来完成整个过程。但是，可在每个数据库中创建一个为该数据库执行聚合的索引视图。如果优化器可将这些索引视图与现有查询匹配，由于无须对现有查询重新编码，至少会加快聚合处理的速度。虽然联接处理速度没有加快，但因为使用存储在索引视图中的聚合，所以总体查询速度将更快。

  • 假设某个经常执行的查询要聚合几个表中的数据，然后使用 UNION 组合结果。一个索引视图中不允许使用 UNION。可再设计一些视图以执行各个聚合操作。这样一来，优化器就可以选择索引视图以加快查询的速度而无须对查询重新编码。尽管 UNION 处理速度没有提高，但每一个聚合进程的速度确实提高了。

设计可满足多个操作的索引视图。因为优化器可使用即使没有在 FROM 子句中指定的索引视图，所以，设计良好的索引视图可加快许多查询的处理速度。

例如，考虑在以下视图上创建索引：

CREATE VIEW ExampleView (PriKey, SumColx, CountColx)
AS
SELECT PriKey, SUM(Colx), COUNT_BIG(Colx)
FROM MyTable
GROUP BY PriKey

该视图不仅能满足直接引用视图列的查询，还可用于满足查询基表并包含表达式如 SUM(Colx)、COUNT_BIG(Colx)、COUNT(Colx) 和 AVG(Colx) 的查询。因为所有这些查询只须在视图中检索少量的行而不需要从基表中读取全部行，所以查询速度将更快。

请参见

在计算列上创建索引

解析视图上的索引

视图索引


在视图上使用索引

Microsoft® SQL Server™ 2000 查询优化器确定给定查询是否能从使用数据库中定义的索引获益。索引包括索引视图和基表索引。当满足下列条件时，SQL Server 查询优化器使用索引视图：

  • 下列会话选项均设置为 ON：
    • ANSI_NULLS

    • ANSI_PADDING

    • ANSI_WARNINGS

    • ARITHABORT

    • CONCAT_NULL_YIELDS_NULL

    • QUOTED_IDENTIFIERS
  • NUMERIC_ROUNDABORT 会话选项设置为 OFF。

  • 优化器查找视图索引列与查询中的元素之间的匹配部分，如：
    • WHERE 子句中的搜索条件谓词。

    • 联接操作。

    • 聚合函数。
  • 估计的索引使用成本是优化器考虑使用的所有访问机制中的最低成本。

除了 SET 选项的要求外，其它规则与优化器确定索引是否覆盖查询的规则相同。查询中无须再指定其它选项以使用索引视图。

此外，也无须为优化器使用索引视图而特地在 FROM 子句中引用此索引视图。如果查询所引用的基表中的列也同时存在于索引视图中，并且，优化器估计使用索引视图将提供最低成本的访问机制，则优化器会选择索引视图，其方式类似于当查询中不直接引用基表索引时选择基表索引。当视图中包含非查询所引用的列时，只要视图提供覆盖一个或多个查询所指定列的最低成本选项，优化器即可能选择视图。

使用 EXPAND VIEWS 选项可防止使用视图索引进行查询。可使用 NOEXPAND 视图提示强制优化器使用在 FROM 子句中指定的索引视图上的索引。但是，最好还是让优化器动态地确定每个查询应使用的最佳访问方法。当测试显示使用 EXPAND 和 NOEXPAND 能极大提高性能时才使用这两个选项。

EXPAND VIEWS 选项指定对于整个查询，优化器不使用任何视图索引。

  • 优化器不使用任何索引视图。当优化器评估覆盖查询中引用的列的最低成本方法时，它将忽略所有视图索引。

  • 优化器视 FROM 子句中引用的索引视图为标准视图。优化器将视图的逻辑纳入查询执行计划中，并从基表中动态生成结果集。优化器忽略在视图上定义的索引。

当指定视图为 NOEXPAND 时，优化器将考虑使用视图上定义的任何索引。通过在可选的 INDEX() 子句中指定 NOEXPAND，可强制优化器使用指定索引。只能为索引视图指定 NOEXPAND，不能为还未编索引的视图指定。

请参见

FROM

解析视图上的索引

SELECT


创建索引视图

视图也称为虚拟表，这是因为由视图返回的结果集其一般格式与由列和行组成的表相似，并且，在 SQL 语句中引用视图的方式也与引用表的方式相同。标准视图的结果集不是永久地存储在数据库中。查询每次引用视图时，Microsoft® SQL Server™ 2000 会动态地将生成视图结果集所需的逻辑合并到从基表数据生成完整查询结果集所需的逻辑中。生成视图结果的过程称为视图具体化。有关更多信息，请参见视图解析。

对于标准视图而言，为每个引用视图的查询动态生成结果集的开销很大，特别是对于那些涉及对大量行进行复杂处理（如聚合大量数据或联接许多行）的视图更为可观。若经常在查询中引用这类视图，可通过在视图上创建唯一聚集索引来提高性能。在视图上创建唯一聚集索引时将执行该视图，并且结果集在数据库中的存储方式与带聚集索引的表的存储方式相同。有关用于存储聚集索引的结构的更多信息，请参见聚集索引。

说明 只有安装了 Microsoft SQL Server 2000 企业版或 Microsoft SQL Server 2000 开发版，才可以创建索引视图。

在视图上创建索引的另一个好处是：查询优化器开始在查询中使用视图索引，而不是直接在 FROM 子句中命名视图。这样一来，可从索引视图检索数据而无需重新编码，由此带来的高效率也使现有查询获益。有关更多信息，请参见在视图上使用索引。

在视图上创建聚集索引可存储创建索引时存在的数据。索引视图还自动反映自创建索引后对基表数据所做的更改，这一点与在基表上创建的索引相同。当对基表中的数据进行更改时，索引视图中存储的数据也反映数据更改。视图的聚集索引必须唯一，从而提高了 SQL Server 在索引中查找受任何数据更改影响的行的效率。

与基表上的索引相比，对索引视图的维护可能更复杂。只有当视图的结果检索速度的效益超过了修改所需的开销时，才应在视图上创建索引。这样的视图通常包括映射到相对静态的数据上、处理多行以及由许多查询引用的视图。

视图的要求

在视图上创建聚集索引之前，该视图必须满足下列要求：

  • 当执行 CREATE VIEW 语句时，ANSI_NULLS 和 QUOTED_IDENTIFIER 选项必须设置为 ON。OBJECTPROPERTY 函数通过 ExecIsAnsiNullsOn 或 ExecIsQuotedIdentOn 属性为视图报告此信息。

  • 为执行所有 CREATE TABLE 语句以创建视图引用的表，ANSI_NULLS 选项必须设置为 ON。

  • 视图不能引用任何其它视图，只能引用基表。

  • 视图引用的所有基表必须与视图位于同一个数据库中，并且所有者也与视图相同。

  • 必须使用 SCHEMABINDING 选项创建视图。SCHEMABINDING 将视图绑定到基础基表的架构。

  • 必须已使用 SCHEMABINDING 选项创建了视图中引用的用户定义的函数。

  • 表和用户定义的函数必须由 2 部分的名称引用。不允许使用 1 部分、3 部分和 4 部分的名称。

  • 视图中的表达式所引用的所有函数必须是确定性的。OBJECTPROPERTY 函数的 IsDeterministic 属性报告用户定义的函数是否是确定性的。有关更多信息，请参见确定性函数和非确定性函数。

  • 视图中的 SELECT 语句不能包含下列 Transact-SQL 语法元素：
    • 选择列表不能使用 * 或 table_name.* 语法指定列。必须显式给出列名。

    • 不能在多个视图列中指定用作简单表达式的表的列名。如果对列的所有（或只有一个例外）引用是复杂表达式的一部分或是函数的一个参数，则可多次引用该列。例如，下列选择列表是非法的：
      SELECT ColumnA, ColumnB, ColumnA

      下列选择列表是合法的：

      SELECT ColumnA, AVG(ColumnA), ColumnA + Column B AS AddColAColB

SELECT SUM(ColumnA), ColumnA % ColumnB AS ModuloColAColB
    • 派生表。

    • 行集函数。

    • UNION 运算符。

    • 子查询。

    • 外联接或自联接。

    • TOP 子句。

    • ORDER BY 子句。

    • DISTINCT 关键字。

    • COUNT(*)（允许 COUNT_BIG(*)。）

    • AVG、MAX、MIN、STDEV、STDEVP、VAR 或 VARP 聚合函数。如果在引用索引视图的查询中指定 AVG、MAX、MIN、STDEV、STDEVP、VAR 或 VARP，如果视图选择列表包含以下替换函数，则优化器会经常计算需要的结果。
      复杂聚合函数 替代简单聚合函数
      AVG(X)

              SUM(X), COUNT_BIG(X)

      STDEV(X)

              SUM(X), COUNT_BIG(X), SUM(X**2)

      STDEVP(X)

              SUM(X), COUNT_BIG(X), SUM(X**2)

      VAR(X)

              SUM(X), COUNT_BIG(X), SUM(X**2)

      VARP(X)

              SUM(X), COUNT_BIG(X), SUM(X**2)

      例如，索引视图选择列表不能包含表达式 AVG(SomeColumn)。如果视图选择列表包含表达式 SUM(SomeColumn) 和 COUNT_BIG(SomeColumn)，则 SQL Server 可为引用视图并指定 AVG(SomeColumn) 的查询计算平均数。

    • 引用可为空的表达式的 SUM 函数。

    • 全文谓词 CONTAINS 或 FREETEXT。

    • COMPUTE 或 COMPUTE BY 子句。
  • 如果没有指定 GROUP BY，则视图选择列表不能包含聚合表达式。

  • 如果指定了 GROUP BY，则视图选择列表必须包含 COUNT_BIG(*) 表达式，并且，视图定义不能指定 HAVING、CUBE 或 ROLLUP。

  • 通过一个既可以取值为 float 值也可以使用 float 表达式求值的表达式而生成的列不能作为索引视图或表的索引的键。
CREATE INDEX 语句的要求

在视图上创建的第一个索引必须是唯一聚集索引。在创建唯一聚集索引后，可创建其它非聚集索引。视图上的索引命名规则与表上的索引命名规则相同。唯一区别是表名由视图名替换。有关更多信息，请参见 CREATE INDEX。

除了一般的 CREATE INDEX 要求外，CREATE INDEX 语句还必须满足下列要求：

  • 执行 CREATE INDEX 语句的用户必须是视图的所有者。

  • 当执行 CREATE INDEX 语句时，下列 SET 选项必须设置为 ON：
    • ANSI_NULLS

    • ANSI_PADDING

    • ANSI_WARNINGS

    • ARITHABORT

    • CONCAT_NULL_YIELDS_NULL

    • QUOTED_IDENTIFIERS
  • 必须将选项 NUMERIC_ROUNDABORT 选项设置为 OFF。

  • 视图不能包含 text、ntext 或 image 列，即使在 CREATE INDEX 语句中没有引用它们。

  • 如果视图定义中的 SELECT 语句指定了一个 GROUP BY 子句，则唯一聚集索引的键只能引用在 GROUP BY 子句中指定的列。
注意事项

创建聚集索引后，对于任何试图为视图修改基本数据而进行的连接，其选项设置必须与创建索引所需的选项设置相同。如果这个执行语句的连接没有适当的选项设置，则 SQL Server 生成错误并回滚任何会影响视图结果集的 INSERT、UPDATE 或 DELETE 语句。有关更多信息，请参见影响结果的 SET 选项。

若除去视图，视图上的所有索引也将被除去。若除去聚集索引，视图上的所有非聚集索引也将被除去。可分别除去非聚集索引。除去视图上的聚集索引将删除存储的结果集，并且优化器将重新象处理标准视图那样处理视图。

尽管 CREATE UNIQUE CLUSTERED INDEX 语句仅指定组成聚集索引键的列，但视图的完整结果集将存储在数据库中。与基表上的聚集索引一样，聚集索引的 B 树结构仅包含键列，但数据行包含视图结果集中的所有列。

若想为现有系统中的视图添加索引，必须计划绑定任何想要放入索引的视图。可以：

  • 除去视图并通过指定 WITH SCHEMABINDING 重新创建它。

  • 创建另一个视图，使其具有与现有视图相同的文本，但是名称不同。优化器将考虑新视图上的索引，即使在查询的 FROM 子句中没有直接引用它。

说明 不能除去参与到用 SCHEMABINDING 子句创建的视图中的表或视图，除非该视图已被除去或更改而不再具有架构绑定。另外，如果对参与具有架构绑定的视图的表执行 ALTER TABLE 语句，而这些语句又会影响视图定义，则这些语句将会失败。

必须确保新视图满足索引视图的所有要求。这可能需要更改视图及其所引用的所有基表的所有权，以便它们都为同一用户所拥有。

请参见

CREATE INDEX

SET ANSI_NULLS

SET ANSI_PADDING

SET ANSI_WARNINGS

SET ARITHABORT

SET CONCAT_NULL_YIELDS_NULL

SET NUMERIC_ROUNDABORT

SET QUOTED_IDENTIFIER


创建分区视图

分区视图在一个或多个服务器间水平连接一组成员表中的分区数据，使数据看起来就象来自一个表。Microsoft® SQL Server™ 2000 区分本地分区视图和分布式分区视图。在本地分区视图中，所有的参与表和视图驻留在同一个 SQL Server 实例上。在分布式分区视图中，至少有一个参与表驻留在不同的（远程）服务器上。此外，SQL Server 2000 还区分可更新的分区视图和作为基础表只读复本的视图。

分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器，但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制使您能够扩大一组服务器，以支持大型的多层 Web 站点的处理需要。有关更多信息，请参见设计联合数据库服务器。

在实现分区视图之前，必须先水平分区表。原始表被分成若干个较小的成员表。每个成员表包含与原始表相同数量的列，并且每一列具有与原始表中的相应列同样的特性（如数据类型、大小、排序规则）。如果正在创建分布式分区视图，则每个成员表分别位于不同的成员服务器上。为了获得最大程度的位置透明度，各个成员服务器上的成员数据库的名称应当是相同的，但不要求非这样。例如：Server1.CustomerDB、Server2.CustomerDB、Server3.CustomerDB。

成员表设计好后，每个表基于键值的范围存储原始表的一块水平区域。键值范围基于分区列中的数据值。每一成员表中的值范围通过分区列上的 CHECK 约束强制，并且范围之间不能重叠。例如，不能使一个表的值范围从 1 到 200000，而另一个表的值范围从 150000 到 300000，因为这样将不清楚哪个表包含 150000 与 200000 之间的值。

例如，正在将一个 Customer 表分区成三个表。这些表的 CHECK 约束为：

-- On Server1:
CREATE TABLE Customer_33
  (CustomerID   INTEGER PRIMARY KEY
                CHECK (CustomerID BETWEEN 1 AND 32999),
  ... -- Additional column definitions)

-- On Server2:
CREATE TABLE Customer_66
  (CustomerID   INTEGER PRIMARY KEY
                CHECK (CustomerID BETWEEN 33000 AND 65999),
  ... -- Additional column definitions)

-- On Server3:
CREATE TABLE Customer_99
  (CustomerID   INTEGER PRIMARY KEY
                CHECK (CustomerID BETWEEN 66000 AND 99999),
  ... -- Additional column definitions)

在创建成员表后，在每个成员服务器上定义一个分布式分区视图，并且每个视图具有相同的名称。这样，引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样，但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。

生成分布式分区视图的方式如下：

  • 在每一个含有在其它成员服务器上执行分布式查询所需连接信息的成员服务器上添加链接服务器定义。这将使得分布式分区视图能够访问其它服务器上的数据。

  • 对于在分布式分区视图中使用的每个链接服务器定义，使用 sp_serveroption 设置 lazy schema validation 选项。这确保了只有在实际需要远程成员表的数据时，查询处理器才请求任何链接表的元数据，从而使性能得到优化。

  • 在每个成员服务器上创建分布式分区视图。这些视图使用分布式 SELECT 语句访问链接成员服务器上的数据，并将分布式行与本地成员表的行合并。

若要为上一个示例创建分布式分区视图，应当：

  • 为 Server2 添加一个名为 Server2 的、带有连接信息的链接服务器定义，并添加一个名为 Server3 的链接服务器定义以访问 Server3。

  • 创建以下分布式分区视图：
    CREATE VIEW Customers AS
   SELECT * FROM CompanyDatabase.TableOwner.Customers_33
UNION ALL
   SELECT * FROM Server2.CompanyDatabase.TableOwner.Customers_66
UNION ALL
   SELECT * FROM Server3.CompanyDatabase.TableOwner.Customers_99
  • 在 Server2 和 Server3 上执行相同的步骤。
可更新的分区视图

如果本地或分布式分区视图为不可更新的，则它只能作为原始表的只读复本。可更新的分区视图可展示出原始表的所有功能。

在下列情况中，视图被视为可更新的分区视图：

  • 视图是一组 SELECT 语句，这些语句的结果集通过 UNION ALL 语句组合为一个结果集。每个 SELECT 语句引用一个 SQL Server 基表。该表可以是本地表，也可以是使用 4 部分名称、OPENROWSET 函数或 OPENDATASOURCE 函数引用的链接表（不能使用 OPENDATASOURCE 或 OPENROWSET 函数指定直接传递式查询）。
表规则

成员表在视图定义中的每个 SELECT 语句的 FROM 子句中定义。每个成员表都必须遵守如下规则：

  • 在视图中每个成员表只能引用一次。

  • 成员表不能有任何计算列上创建的索引。

  • 成员表在数目相同的列上应具有所有 PRIMARY KEY 约束。

  • 成员表必须有相同的 ANSI 填充设置。有关 ANSI 填充设置的更多信息，请参见 SET ANSI_PADDING。
列规则

列在视图定义中的每个 SELECT 语句的选择列表中定义。列必须遵守如下规则。

  • 每个成员表中的所有列必须包含在选择列表中。

  • 在选择列表中不能多次使用同一列。

  • 在选择列表中对列的引用不能多于一次。

  • 列必须位于选择列表中的相同序号位置处。

  • 每个 SELECT 语句的选择列表中的列必须是同一类型（包括数据类型、精度、小数位数和排序规则）。例如，以下视图定义失败，这是因为两个 SELECT 语句中首列的数据类型不同：
    CREATE VIEW NonUpdatable
AS
SELECT IntPrimaryKey, IntPartNmbr
FROM FirstTable
  UNION ALL
SELECT NumericPrimaryKey, IntPartNmbr
FROM SecondTable
分区列规则

分区列存在于每个成员表上，并且通过 CHECK 约束标识特定表中的可用数据。分区列必须遵守如下规则：

  • 每个基表都拥有键值由 CHECK 约束所强制的分区列。每个表的 CHECK 约束的键范围与其它任何表互不重叠。任何分区列的给定值必须只能映射到一个表。CHECK 约束只能使用以下运算符：BETWEEN、AND、OR、<、<=、>、>=、=。

  • 在视图中，分区列必须位于每个 SELECT 语句的选择列表中相同的序号位置处。例如，分区列要么总是每个选择列表中的首列，要么总是每个选择列表中的第二列，依次类推。

  • 分区列不允许为空。

  • 分区列必须是表的主键的一部分。

  • 分区列不能是计算列。

  • 在分区列上必须仅有一个约束。如果有多于一个的约束，SQL Server 会忽略所有的约束并在确定视图是否为分区视图时不考虑这些约束。

满足所有上述规则的分区列将支持 SQL Server 2000 查询优化器支持的所有优化。有关更多信息，请参见解析分布式分区视图。

数据修改规则

除了为可更新的分区视图定义的规则外，引用该视图的数据修改语句还必须遵守为 INSERT、UPDATE 和 DELETE 语句定义的规则。

说明 只有在安装了 Microsoft SQL Server 2000 企业版或 Microsoft SQL Server 2000 开发版时，才可通过分区视图修改数据。

INSERT 语句

INSERT 语句通过分区视图将数据添加到成员表中。INSERT 语句必须遵守下列规则：

  • 所有列必须包含在 INSERT 语句中，即使基表中的列可能为 NULL 或在基表中定义了 DEFAULT 约束。

  • 不能在 INSERT 语句的 VALUES 子句中指定 DEFAULT 关键字。

  • INSERT 语句提供的值必须符合在一个成员表的分区列上定义的 CHECK 约束逻辑。

  • 如果一个成员表包含具有标识属性的列，则不能使用 INSERT 语句。

  • 如果一个成员表包含 timestamp 列，则不能使用 INSERT 语句。

  • 如果存在具有同一视图或任一成员表的自联接，则不能使用 INSERT 语句。
UPDATE 语句

UPDATE 语句通过分区视图在一个或多个成员表中修改数据。UPDATE 语句必须遵守下列规则：

  • UPDATE 语句不能在 SET 子句中将 DEFAULT 关键字指定为值，即使列在相应的成员表中定义了 DEFAULT 值。

  • 不能更改具有标识属性的列的值；不过可以更新其它列。

  • 如果列中包含 text、image 或 ntext 数据，则不能更改 PRIMARY KEY 的值。

  • 如果基表中包含 timestamp 列，则不能进行更新。

  • 如果存在具有同一视图或成员表的自联接，则不能进行更新。

  • 不能在 UPDATE 语句的 SET 子句中指定 DEFAULT 关键字。
DELETE 语句

DELETE 语句通过分区视图在一个或多个成员表中删除数据。DELETE 语句必须遵守如下规则：

  • 如果存在具有同一视图或任一成员表的自联接，则不能使用 DELETE 语句。
分布式分区视图规则

除了为分区视图定义的规则外，分布式（远程）分区视图还有下列附加条件：

  • 将启动分布式事务以确保更新所影响的所有节点间的原子性。

  • XACT_ABORT SET 选项必须设置为 ON。

  • 远程表中的 smallmoney 和 smalldatetime 列分别映射为 money 和 datetime。因此，本地表中的相应列也应为 money 和 datetime。

  • 任何链接服务器都不能是环回链接服务器，即指向同一 SQL Server 实例的链接服务器。

如果视图中含有 INSTEAD OF 触发器，则不遵循上述规则而引用分区表的视图可能仍然是可更新的。但是，查询优化器不能总能够为含有 INSTEAD OF 触发器的视图生成和遵循所有上述规则的分区视图一样有效的执行计划。

请参见

CREATE VIEW

设计分区

视图使用方案

使用分区视图

。


修改和重命名视图

在完成视图定义后，可以在不除去和重新创建视图的条件下更改视图名称或修改其定义，从而丢失与之相关联的权限。在重命名视图时，请遵循以下原则：

  • 要重命名的视图必须位于当前数据库中。

  • 新名称必须遵守标识符规则。

  • 只能重命名自己拥有的视图。

  • 数据库所有者可以更改任何用户视图的名称。

更改视图并不影响相关对象，如存储过程或触发器，除非视图定义的更改使该相关对象不再有效。例如，pubs 数据库中 authors_view 视图的定义方式如下：

CREATE VIEW authors_view
AS
   SELECT au_id FROM authors

存储过程 authors_proc 的定义方式为：

CREATE PROC authors_proc
AS
   SELECT au_id from authors_view

对 authors_view 进行修改以检索 au_lname 列而非 au_id：

ALTER VIEW authors_view
AS
   SELECT au_lname FROM authors

执行时 authors_proc 将会失败，因为视图中 au_id 列已不复存在。

也可以修改视图以对其定义进行加密，或确保所有在视图上执行的数据修改语句都符合定义视图的 SELECT 语句中设定的条件。有关更多信息，请参见创建视图。

修改视图

Transact-SQL

企业管理器

企业管理器

SQL-DMO

重命名视图

Transact-SQL

企业管理器

企业管理器

SQL-DMO

说明 重命名视图并不更改它在视图定义文本中的名称。要在定义中更改视图名称，应直接修改视图。


通过视图修改数据

可以按下列方式通过视图修改数据：

  • 使用具有支持 INSERT、UPDATE 和 DELETE 语句的逻辑的 INSTEAD OF 触发器。

  • 使用修改一个或多个成员表的可更新分区视图。

如果视图不使用 INSTEAD OF 触发器或者不是可更新分区视图，则只要符合下列条件，仍可更新：

如果视图不使用 INSTEAD OF 触发器或者不是可更新分区视图，则只要符合下列条件，仍可更新：

  • 视图在视图定义的 FROM 子句中包含至少一个表；视图不能只基于一个表达式。

  • 在选择列表中没有使用聚合函数（AVG、COUNT、SUM、MIN、MAX、GROUPING、STDEV、STDEVP、VAR、VARP）或 GROUP BY、UNION、DISTINCT 或 TOP 子句。但是，可以在 FROM 子句定义的子查询范围内使用聚合函数，前提条件是聚合函数生成的派生值没有修改过。

    说明 使用 UNION ALL 运算符的分区视图是可更新视图。

  • 在选择列表中不使用派生列。派生列是由除简单列引用以外的一切构成的结果集列。
通过视图修改数据的准则

不使用 INSTEAD OF 触发器或可更新分区视图而通过视图修改数据之前，请考虑下列准则：

  • 如果在视图定义中使用了 WITH CHECK OPTION 子句，则所有在视图上执行的数据修改语句都必须符合定义视图的 SELECT 语句中所设定的条件。如果使用了 WITH CHECK OPTION 子句，修改行时需注意不让它们在修改完成后从视图中消失。任何可能导致行消失的修改都会被取消，并显示错误信息。

  • SQL Server 必须能够明确地解析对视图所引用基表中的特定行所做的修改操作。不能在一个语句中对多个基础表使用数据修改语句。因此，列在 UPDATE 或 INSERT 语句中的列必须属于视图定义中的同一个基表。

  • 对于基础表中需更新而又不允许空值的所有列，它们的值在 INSERT 语句或 DEFAULT 定义中指定。这将确保基础表中所有需要值的列都可以获取值。

  • 在基础表的列中修改的数据必须符合对这些列的约束，如为空性、约束、DEFAULT 定义等。例如，如果要删除一行，则相关表中的所有基础 FOREIGN KEY 约束必须仍然得到满足，删除操作才能成功。

  • 分布式分区视图（远程视图）不能使用键集驱动游标更新。此项限制可通过在基础表上而不在视图本身上声明游标得到解决。

此外，如果在视图中删除数据：

  • 在视图定义的 FROM 子句中只能列出一个表。

不能在视图中与text、ntext 或 image 列一起使用 READTEXT 和 WRITETEXT 语句。

通过视图添加数据

Transact-SQL

通过视图更改数据

Transact-SQL

通过视图删除数据

Transact-SQL


获取关于视图的信息

如果视图定义没有加密，即可获取该视图定义的有关信息。您可能需要查看视图定义以了解数据从源表中的提取方式，或查看视图所定义的数据。

视图的查询方式与普通表的查询方式相同。但是，查询视图时所使用的任何表提示均被忽略。有关表提示的更多信息，请参见 SELECT。

如果更改视图所引用对象的名称，则必须更改视图，使其文本反映新的名称。因此，在重命名对象之前，首先显示该对象的相关情况，以确定即将发生的更改是否会影响任何视图。

获取关于视图的信息

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看由视图定义的数据

Transact-SQL

企业管理器

显示视图的相关性

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

查询基础知识


删除视图

在创建视图后，如果不再需要该视图，或想清除视图定义及与之相关联的权限，可以删除该视图。删除视图后，表和视图所基于的数据并不受到影响。任何使用基于已删除视图的对象的查询将会失败，除非创建了同样名称的一个视图。但是，如果新视图没有引用与之相关的对象所期待的对象，使用相关对象的查询在执行时将会失败。例如，假设删除了检索 pubs 数据库内 authors 表中所有列的一个视图 my_view，并用检索 titles 表中所有列的新视图 my_view 代替它。现在，任何引用 my_view 的基础表 authors 中的列的存储过程都将失败，因为这些列已由来自 titles 表中的列所取代。

删除视图

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

修改和重命名视图

设计存储过程


存储过程

在使用 Microsoft® SQL Server™ 2000 创建应用程序时，Transact-SQL 编程语言是应用程序和 SQL Server 数据库之间的主要编程接口。使用 Transact-SQL 程序时，可用两种方法存储和执行程序。可以在本地存储程序，并创建向 SQL Server 发送命令并处理结果的应用程序；也可以将程序在 SQL Server 中存储为存储过程，并创建执行存储过程并处理结果的应用程序。

SQL Server 中的存储过程与其它编程语言中的过程类似，原因是存储过程可以：

  • 接受输入参数并以输出参数的形式将多个值返回至调用过程或批处理。

  • 包含执行数据库操作（包括调用其它过程）的编程语句。

  • 向调用过程或批处理返回状态值，以表明成功或失败（以及失败原因）。

可使用 Transact-SQL EXECUTE 语句运行存储过程。存储过程与函数不同，因为存储过程不返回取代其名称的值，也不能直接用在表达式中。

使用 SQL Server 中的存储过程而不使用存储在客户计算机本地的 Transact-SQL 程序的优势有：

  • 允许模块化程序设计。

    只需创建过程一次并将其存储在数据库中，以后即可在程序中调用该过程任意次。存储过程可由在数据库编程方面有专长的人员创建，并可独立于程序源代码而单独修改。

  • 允许更快执行。

    如果某操作需要大量 Transact-SQL 代码或需重复执行，存储过程将比 Transact-SQL 批代码的执行要快。将在创建存储过程时对其进行分析和优化，并可在首次执行该过程后使用该过程的内存中版本。每次运行 Transact-SQL 语句时，都要从客户端重复发送，并且在 SQL Server 每次执行这些语句时，都要对其进行编译和优化。

  • 减少网络流量。

    一个需要数百行 Transact-SQL 代码的操作由一条执行过程代码的单独语句就可实现，而不需要在网络中发送数百行代码。

  • 可作为安全机制使用。

    即使对于没有直接执行存储过程中语句的权限的用户，也可授予他们执行该存储过程的权限。

SQL Server 存储过程是用 Transact-SQL 语句 CREATE PROCEDURE 创建的，并可用 ALTER PROCEDURE 语句进行修改。存储过程定义包含两个主要组成部分：过程名称及其参数的说明，以及过程的主体（其中包含执行过程操作的 Transact-SQL 语句）。

请参见

目录存储过程

系统存储过程


扩展存储过程

扩展存储过程使您得以使用象 C 这样的编程语言创建自己的外部例程。对用户来说，扩展存储过程与普通存储过程一样，执行方法也相同。可将参数传递给扩展存储过程，扩展存储过程可返回结果，也可返回状态。扩展存储过程可用于扩展 Microsoft® SQL Server™ 2000 的功能。

扩展存储过程是 SQL Server 可以动态装载并执行的动态链接库 (DLL)。扩展存储过程直接在 SQL Server 的地址空间运行，并使用 SQL Server 开放式数据服务 (ODS) API 编程。

编写好扩展存储过程后，固定服务器角色 sysadmin 的成员即可在 SQL Server 中注册该扩展存储过程，然后授予其他用户执行该过程的权限。扩展存储过程只能添加到 master 数据库中。

说明 扩展存储过程可能会产生内存泄漏或其它降低服务器的性能及可靠性的问题。应考虑将扩展存储过程存储在一个不同于包含被引用数据并使用分布式查询访问数据库的实例的 SQL Server 实例中。有关更多信息，请参见分布式查询。

添加扩展存储过程

Transact-SQL

企业管理器

企业管理器

请参见

向 SQL Server 添加扩展存储过程

扩展存储过程的工作原理

创建扩展存储过程

扩展存储过程示例

调试扩展存储过程


创建存储过程

可使用 Transact-SQL 语句 CREATE PROCEDURE 创建存储过程。创建存储过程前，请考虑下列事项：

  • 不能将 CREATE PROCEDURE 语句与其它 SQL 语句组合到单个批处理中。

  • 创建存储过程的权限默认属于数据库所有者，该所有者可将此权限授予其他用户。

  • 存储过程是数据库对象，其名称必须遵守标识符规则。

  • 只能在当前数据库中创建存储过程。

创建存储过程时，应指定：

  • 所有输入参数和向调用过程或批处理返回的输出参数。

  • 执行数据库操作（包括调用其它过程）的编程语句。

  • 返回至调用过程或批处理以表明成功或失败（以及失败原因）的状态值。
系统存储过程

Microsoft® SQL Server™ 2000 中的许多管理活动是通过一种称为系统存储过程的特殊过程执行的。系统存储过程在 master 数据库中创建并存储，带有 sp_ 前缀。可从任何数据库中执行系统存储过程，而无需使用 master 数据库名称来完全限定该存储过程的名称。

强烈建议您不要创建以 sp_ 为前缀的存储过程。SQL Server 始终按照下列顺序查找以 sp_ 开头的存储过程：

  1. 在 master 数据库中查找存储过程。

  2. 根据所提供的任何限定符（数据库名称或所有者）查找该存储过程。

  3. 如果未指定所有者，则使用 dbo 作为所有者查找该存储过程。

因此，虽然当前数据库中可能存在带 sp_ 前缀的用户创建的存储过程，但总会先检查 master 数据库（即使该存储过程已用数据库名称限定）。

重要 如果用户创建的存储过程与系统存储过程同名，则永远不执行用户创建的存储过程。

分组

如果将一个不同的标识号赋予某过程，则可以用与现有某存储过程相同的名称创建该过程，这样可允许将这些过程进行逻辑分组。同名的分组过程可以同时删除。在同一应用程序中使用的过程一般都以该方式分组。例如，用于 my_app 应用程序的过程可能被命名为 my_proc;1、my_proc;2 等。删除 my_proc 即删除该整个组。将过程分组后，就无法删除该组内的单个过程。

临时存储过程

专用和全局临时存储过程与临时表类似，都可以用向该过程名称添加 # 和 ## 前缀的方法进行创建。# 表示本地临时存储过程，## 表示全局临时存储过程。SQL Server 关闭后，这些过程将不再存在。

临时存储过程在连接到 SQL Server 的早期版本时很有用，这些早期版本不支持再次使用 Transact-SQL 语句或批处理执行计划。连接到 SQL Server 2000 的应用程序应使用 sp_executesql 系统存储过程，而不使用临时存储过程。有关更多信息，请参见执行计划的高速缓存和重新使用。

只有创建本地临时过程的连接才能执行该过程，当该连接关闭（用户从 SQL Server 中注销）时，将自动删除该过程。

任何连接都可执行全局临时存储过程。只有创建该过程的用户所用的连接关闭，并且所有其它连接所用的该过程的当前执行版本运行完毕后，全局临时存储过程才不再存在。一旦用于创建该过程的连接关闭，将不再允许启动执行该全局临时存储过程。只允许那些已启动执行该存储过程的连接完成该过程的运行。

如果直接在 tempdb 数据库中创建没有 # 或 ## 前缀的存储过程，则由于每次启动 SQL Server 时 tempdb 都要重新创建，因此当关闭 SQL Server 时将自动删除该存储过程。直接在 tempdb 中创建的过程即使在创建该过程的连接终止后也会存在。与任何其它对象一样，可向其他用户授予、拒绝和废除执行该临时存储过程的权限。

创建存储过程

Transact-SQL

企业管理器

企业管理器

SQL-DMO

也可以使用 SQL Server 企业管理器的创建存储过程向导来创建存储过程。

用创建存储过程向导创建存储过程

SQL-DMO

SQL-DMO

请参见

向 SQL Server 添加扩展存储过程

设计存储过程

创建扩展存储过程

sp_executesql

重新编译存储过程

系统存储过程


指定参数

存储过程通过其参数与调用程序通讯。当程序执行存储过程时，可通过存储过程的参数向该存储过程传递值。这些值可作为 Transact-SQL 编程语言中的标准变量使用。存储过程也可通过 OUTPUT 参数将值返回至调用程序。一个存储过程可有多达 2100 个参数，每个参数都有名称、数据类型、方向和默认值。

请参见

参数

使用 OUTPUT 参数返回数据


指定名称

每个存储过程参数都必须用唯一的名称进行定义。与标准的 Transact-SQL 变量相同，存储过程名称必须以单个 @ 字符开头，并且必须遵从对象标识符规则。可在存储过程中使用参数名称以获得参数值并更改它。

既可以通过显式指定参数名称并赋予适当值，也可以通过提供在 CREATE PROCEDURE 语句中给定的参数值（不指定参数名称）来向存储过程传递值。例如，如果存储过程 my_proc 应有三个命名为 @first、@second 和 @third 的参数，则可将传递至该存储过程的值赋予参数名称，如：

EXECUTE my_proc @second = 2, @first = 1, @third = 3

或者按照位置传递，而不命名参数名称：

EXECUTE my_proc 1, 2, 3

执行存储过程时指定参数名称，将允许按任何顺序提供参数。如果未指定参数名称，那么必须按照与定义存储过程参数时相同的顺序（从左至右）提供参数。另外，必须提供给定参数前面的所有参数，即使这些参数可选且有默认值。例如，如果 my_proc 的参数都是可选的，那么执行 my_proc 时可以仅提供第一个和第二个参数的值，但不能仅提供第二个和第三个参数的值。这是必需的，否则 Microsoft® SQL Server™ 2000 将无法识别正在被指定的参数。

请参见

EXECUTE

使用标识符

指定默认值


指定数据类型

存储过程中的参数要定义数据类型，这与表中的列几乎一样。可以用 Microsoft® SQL Server™ 2000 的任何一种数据类型（包括 text 和 image 类型）定义存储过程参数。也可以用用户定义的数据类型定义存储过程参数。

说明 对于存储过程来说，数据类型 cursor 只能用作 OUTPUT 参数。有关使用游标变量的更多信息，请参见 Transact-SQL 游标名称的作用域。

参数的数据类型确定了该参数所接受值的类型和范围。例如，如果用 tinyint 数据类型定义参数，则该参数将只接受 0 至 255 范围内的数值。如果用与数据类型不兼容的值执行存储过程，将返回一个错误。

请参见

创建用户定义的数据类型

CREATE PROCEDURE

数据类型


指定参数的方向

当调用存储过程的程序执行该存储过程时，所有过程参数都可接收输入值。

示例

下列存储过程 get_sales_for_title 使用了输入参数。该存储过程中的 @title 参数接收调用程序所指定的书名作为输入值。SELECT 语句使用此 @title 参数获得正确的 ytd_sales 值并显示该值。

CREATE PROCEDURE get_sales_for_title
@title varchar(80)   -- This is the input parameter.
AS

-- Get the sales for the specified title.
SELECT "YTD_SALES" = ytd_sales
FROM titles
WHERE title = @title

RETURN
GO

如果在存储过程定义中为某参数指定 OUTPUT 关键字，则存储过程将在其退出时向调用程序返回此参数的当前值。调用程序也必须使用 OUTPUT 关键字执行该存储过程，才能将该参数值保存到变量中以便在调用程序中使用。有关更多信息，请参见使用 OUTPUT 参数返回数据。


指定默认值

通过为可选参数指定默认值，可创建带有可选参数的存储过程。执行该存储过程时，如果未指定其它值，则使用默认值。

如果在存储过程中没有指定参数的默认值，并且调用程序也没有在执行存储过程时为该参数提供值，那么会返回系统错误，因此指定默认值是必要的。

如果不能为参数指定合适的默认值，则可以指定 NULL 作为参数的默认值，并在未提供参数值而执行存储过程的情况下，使存储过程返回一条自定义消息。

说明 如果默认值是包含嵌入空格或标点符号的字符串，或者以数字开头（例如,6xxx），那么该默认值必须用直的单引号引起来。

示例

下例显示在未提供 @title 参数值的情况下执行存储过程时，get_sales_for_title 过程将进行特殊处理。

CREATE PROCEDURE get_sales_for_title
@title varchar(80) = NULL,  -- NULL default value
@ytd_sales int OUTPUT
AS

-- Validate the @title parameter.
IF @title IS NULL
BEGIN
   PRINT 'ERROR: You must specify a title value.'
   RETURN
END

-- Get the sales for the specified title and
-- assign it to the output parameter.
SELECT @ytd_sales = ytd_sales
FROM titles
WHERE title = @title

RETURN
GO

下例显示三个参数 @first、@second 和 @third 均有默认值的过程 my_proc，以及在用其它参数值执行该存储过程时所显示的值：

CREATE PROCEDURE my_proc
@first int = NULL,  -- NULL default value
@second int = 2,    -- Default value of 2
@third int = 3      -- Default value of 3
AS

-- Display values.
SELECT @first, @second, @third
GO

EXECUTE my_proc                -- No parameters supplied
GO

显示：

NULL  2  3

EXECUTE my_proc 10, 20, 30     -- All parameters supplied
GO

显示：

10  20  30

EXECUTE my_proc @second = 500  -- Only second parameter supplied by name
GO

显示：

NULL  500  3

EXECUTE my_proc 40, @third = 50 -- Only first and third parameters
GO                              -- are supplied.

显示：

40  2  50

请参见

EXECUTE


设计存储过程

几乎任何可写成批处理的 Transact-SQL 代码都可用于创建存储过程。

存储过程的设计规则

存储过程的设计规则包括：

  • CREATE PROCEDURE 定义本身可包括除下列 CREATE 语句以外的任何数量和类型的 SQL 语句，存储过程中的任意地方都不能使用下列语句：
    CREATE DEFAULT | CREATE TRIGGER
    CREATE PROCEDURE | CREATE VIEW
    CREATE RULE

  • 可在存储过程中创建其它数据库对象。可以引用在同一存储过程中创建的对象，前提是在创建对象后再引用对象。

  • 可以在存储过程内引用临时表。

  • 如果在存储过程内创建本地临时表，则该临时表仅为该存储过程而存在；退出该存储过程后，临时表即会消失。

  • 如果执行调用其它存储过程的存储过程，那么被调用存储过程可以访问由第一个存储过程创建的、包括临时表在内的所有对象。

  • 如果执行在远程 Microsoft® SQL Server™ 2000 实例上进行更改的远程存储过程，则不能回滚这些更改。远程存储过程不参与事务处理。

  • 存储过程中参数的最大数目为 2100。

  • 存储过程中局部变量的最大数目仅受可用内存的限制。

  • 根据可用内存的不同，存储过程的最大大小可达 128 MB。

有关创建存储过程的规则的更多信息，请参见 CREATE PROCEDURE。

限定存储过程内的名称

在存储过程内部，如果用于诸如 SELECT 或 INSERT 这样的语句的对象名没有限定用户，那么用户将默认为该存储过程的所有者。在存储过程内部，如果创建存储过程的用户没有限定 SELECT、INSERT、UPDATE 或 DELETE 语句中引用的表名，那么通过该存储过程对这些表进行的访问将默认地受到该过程的创建者权限的限制。

如果有其他用户要使用存储过程，则用于语句 ALTER TABLE、CREATE TABLE、DROP TABLE、TRUNCATE TABLE、CREATE INDEX、DROP INDEX、UPDATE STATISTICS 和 DBCC 的对象名必须用该对象所有者的名称限定。例如，Mary 拥有表 marytab，如果她希望其他用户能够执行使用该表的存储过程，必须在该表用于上述某一条语句时对其表名进行限定。

此规则是必需的，因为运行存储过程时将解析对象的名称。如果未限定 marytab，而 John 试图执行该过程，SQL Server 将查找 John 所拥有的名为 marytab 的表。

加密过程定义

如果要创建存储过程，并且希望确保其他用户无法查看该过程的定义，那么可以使用 WITH ENCRYPTION 子句。这样，过程定义将以不可读的形式存储。

存储过程一旦加密其定义即无法解密，任何人（包括存储过程的所有者或系统管理员）都将无法查看存储过程定义。

SET 语句选项

当 ODBC 应用程序与 SQL Server 连接时，服务器将自动设置会话的下列选项：

  • SET QUOTED_IDENTIFIER ON

  • SET TEXTSIZE 2147483647

  • SET ANSI_DEFAULTS ON

  • SET CURSOR_CLOSE_ON_COMMIT OFF

  • SET IMPLICIT_TRANSACTIONS OFF

这些设置将提高 ODBC 应用程序的可移植性。由于基于 DB-Library 的应用程序通常不设置这些选项，所以应在上述所列 SET 选项打开和关闭的情况下都对存储过程进行测试。这样可确保存储过程始终能正确工作，而不管特定的连接在唤醒调用该存储过程时可能设置的选项。需要特别设置其中一个选项的存储过程，应在开始该存储过程时发出一条 SET 语句。此 SET 语句将只对该存储过程的执行保持有效，当该存储过程结束时，将恢复原设置。

示例
A. 创建使用参数的存储过程

下例创建一个在 pubs 数据库中很有用的存储过程。给出一个作者的姓和名，该存储过程将显示该作者的每本书的标题和出版商。

CREATE PROC au_info @lastname varchar(40), @firstname varchar(20)
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors INNER JOIN titleauthor ON authors.au_id = titleauthor.au_id
   JOIN titles ON titleauthor.title_id = titles.title_id
   JOIN publishers ON titles.pub_id = publishers.pub_id
WHERE au_fname = @firstname
AND au_lname = @lastname
GO

将出现一条说明该命令未返回任何数据也未返回任何行的消息，这表示已创建该存储过程。

现在执行 au_info 存储过程：

EXECUTE au_info Ringer, Anne
GO

下面是结果集：

au_lname | au_fname | title | pub_name
--------- | --------- | --------------------- | ----------------
Ringer | Anne | The Gourmet Microwave | Binnet & Hardley
Ringer | Anne | Is Anger the Enemy? | New Moon Books

(2 row(s) affected)
B. 创建使用参数默认值的存储过程

下例创建一个存储过程 pub_info2，该存储过程显示作为参数给出的出版商所出版的某本书的作者姓名。如果未提供出版商的名称，该存储过程将显示由 Algodata Infosystems 出版的书籍的作者。

CREATE PROC pub_info2 @pubname varchar(40) = 'Algodata Infosystems'
AS
SELECT au_lname, au_fname, pub_name
FROM authors a INNER JOIN titleauthor ta ON a.au_id = ta.au_id
   JOIN titles t ON ta.title_id = t.title_id
   JOIN publishers p ON t.pub_id = p.pub_id
WHERE @pubname = p.pub_name

执行未指定参数的 pub_info2：

EXECUTE pub_info2
GO

下面是结果集：

au_lname | au_fname | pub_name
---------------- | ---------------- | --------------------
Green | Marjorie | Algodata Infosystems
Bennet | Abraham | Algodata Infosystems
O'Leary | Michael | Algodata Infosystems
MacFeather | Stearns | Algodata Infosystems
Straight | Dean | Algodata Infosystems
Carson | Cheryl | Algodata Infosystems
Dull | Ann | Algodata Infosystems
Hunter | Sheryl | Algodata Infosystems
Locksley | Charlene | Algodata Infosystems

(9 row(s) affected)
C. 执行用显式值替代参数默认值的存储过程

在下例中，存储过程 showind2 的 @table 参数默认值是 titles。

CREATE PROC showind2 @table varchar(30) = 'titles'
AS
SELECT TABLE_NAME = sysobjects.name,
INDEX_NAME = sysindexes.name, INDEX_ID = indid
FROM sysindexes INNER JOIN sysobjects ON sysobjects.id = sysindexes.id
WHERE sysobjects.name = @table

列标题（例如，TABLE_NAME）可使结果更具可读性。下面是该存储过程显示的 authors 表的情况：

EXECUTE showind2 authors
GO

TABLE_NAME | INDEX_NAME | INDEX_ID
---------- | ---------- | ----------
authors | UPKCL_auidind | 1
authors | aunmind | 2

(2 row(s) affected)

如果用户未提供值，则 SQL Server 将使用默认表 titles：

EXECUTE showind2
GO

下面是结果集：

TABLE_NAME | INDEX_NAME | INDEX_ID
---------- | ---------- | ----------
titles | UPKCL_titleidind | 1
titles | titleind | 2

(2 row(s) affected)
D. 使用参数默认值 NULL 创建存储过程

参数默认值可以是 NULL 值。在这种情况下，如果未提供参数，则 SQL Server 将根据存储过程的其它语句执行存储过程。不会显示错误信息。

过程定义还可指定当不给出参数时要采取的其它某种措施。例如：

CREATE PROC showind3 @table varchar(30) = NULL
AS IF @table IS NULL
   PRINT 'Give a table name'
ELSE
   SELECT TABLE_NAME = sysobjects.name,
   INDEX_NAME = sysindexes.name, INDEX_ID = indid
   FROM sysindexes INNER JOIN sysobjects
   ON sysobjects.id = sysindexes.id
   WHERE sysobjects.name = @table
E. 使用包含通配符的参数默认值创建存储过程

如果存储过程将参数用于 LIKE 关键字，那么默认值可包括通配符（%、_、[] 和 [^]）。例如，可将 showind 修改为当不提供参数时显示有关系统表的信息：

CREATE PROC showind4 @table varchar(30) = 'sys%'
AS SELECT TABLE_NAME = sysobjects.name,
   INDEX_NAME = sysindexes.name, INDEX_ID = indid
FROM sysindexes INNER JOIN sysobjects
ON sysobjects.id = sysindexes.id
WHERE sysobjects.name LIKE @table

在存储过程 au_info 的下列变化形式中，两个参数都有带通配符的默认值：

CREATE PROC au_info2 @lastname varchar(30) = 'D%',
   @firstname varchar(18) = '%'
AS
SELECT au_lname, au_fname, title, pub_name
FROM authors INNER JOIN titleauthor ON authors.au_id = titleauthor.au_id
   JOIN titles ON titleauthor.title_id = titles.title_id
   JOIN publishers ON titles.pub_id = publishers.pub_id
WHERE au_fname LIKE @firstname
   AND au_lname LIKE @lastname

如果执行 au_info2 时不指定参数，将显示姓以字母 D 开头的所有作者：

EXECUTE au_info2
GO

下面是结果集：

au_lname | au_fname | title | pub_name
-------- | -------- | --------------------- | -------------------
Dull | Ann | Secrets of Silicon Val | Algodata Infosystems
del Castillo | Innes | Silicon Val Gastrono | Binnet & Hardley
DeFrance | Michel | The Gourmet Microwave | Binnet & Hardley

(3 row(s) affected)

下例在两个参数的默认值已定义的情况下，省略了第二个参数，因此可找到姓为 Ringer 的所有作者的书和出版商：

EXECUTE au_info2 Ringer
GO

au_lname | au_fname | title | pub_name
--------- | --------- | ---------------------- | ----------------
Ringer | Anne | The Gourmet Microwave | Binnet & Hardley
Ringer | Anne | Is Anger the Enemy? | New Moon Books
Ringer | Albert | Is Anger the Enemy? | New Moon Books
Ringer | Albert | Life Without Fear | New Moon Books

(4 row(s) affected)

请参见

CREATE PROCEDURE

EXECUTE

SQL-92 选项的作用

在存储过程和触发器中回滚


嵌套存储过程

当一个存储过程调用另一个存储过程时，存储过程就会嵌套。存储过程最多可以嵌套 32 级。被调用存储过程开始执行时，嵌套级数增加一级；被调用存储过程完成执行时，嵌套级数减少一级。试图超出 32 级的最高嵌套级数时，将导致整个存储过程调用链的失败。正在执行的存储过程的当前嵌套级数存储在 @@NESTLEVEL 函数中。

尽管嵌套限制为 32 级，但 Microsoft® SQL Server™ 2000 对可从给定存储过程中唤醒调用的存储过程数没有限制，只要从属存储过程不唤醒调用其它从属存储过程，且永远不会超过最大嵌套级数。

嵌套存储过程中的错误对调用存储过程来讲未必是严重的。当在存储过程内唤醒调用存储过程时，可使用 Transact-SQL 语句 RETURN 返回一个返回代码，并在调用存储过程中检查这个返回代码。这样，就可以指定发生错误时存储过程的行为。有关使用返回代码的更多信息，请参见使用返回代码返回数据。

存储过程甚至可以嵌套调用其自身，这种技术称为递归。

请参见

@@NESTLEVEL


延迟名称解析和编译

创建存储过程时，要分析该过程中的语句以检查语法的准确性。如果在过程定义中遇到语法错误，将返回错误，并且将不创建该存储过程。如果语句的语法正确，则存储过程的文本将存储在 syscomments 系统表中。

当首次执行存储过程时，查询处理器从该过程的 syscomments 系统表中读取该存储过程的文本，并检查该过程所使用的对象名称是否存在。这一过程称为延迟名称解析，因为存储过程引用的对象不需要在创建该存储过程时就存在，而只需在执行该存储过程时存在。

在解析阶段，Microsoft® SQL Server™ 2000 还执行其它验证活动（例如，检查列数据类型与变量的兼容性）。如果执行存储过程时存储过程所引用的对象丢失，则存储过程在到达引用丢失对象的语句时将停止执行。在这种情况下，或在解析阶段发现其它错误时，将返回错误信息。

说明 如果删除或重命名了存储过程所引用的对象，则在执行该存储过程时会返回错误。但如果用同名的对象替换了存储过程中引用的对象，那么无须重新编译即可执行该存储过程。例如，如果存储过程 proc1 引用了表 test1，而 test1 被删除，并创建了名为 test1 的另一个表，那么 proc1 将引用该新表。不必重新编译该存储过程。

如果过程执行顺利通过解析阶段，则 SQL Server 中的查询优化器将分析该存储过程中的 Transact-SQL 语句，并创建一个执行计划。执行计划描述执行存储过程的最快方法，所依据的信息包括：

  • 表中的数据量。

  • 表中是否存在索引及索引的性质，以及数据在索引列中的分布。

  • WHERE 子句条件所用的比较运算符和比较值。

  • 是否存在联接以及 UNION、GROUP BY 和 ORDER BY 关键字。

查询优化器在分析完存储过程中的这些因素后，将执行计划置于内存中。分析存储过程和创建执行计划的过程称为编译。优化的内存中的执行计划将用来执行该查询。执行计划将驻留在内存中，直到重新启动 SQL Server 或需要空间以存储另一个对象时为止。

在随后执行存储过程时，如果现有的执行计划仍在内存中，则 SQL Server 将再次使用它。如果执行计划不再位于内存中，则创建新的执行计划。有关更多信息，请参见存储过程和触发器执行。

请参见

CREATE PROCEDURE

执行计划的高速缓存和重新使用


从存储过程中返回数据

Microsoft® SQL Server™ 2000 存储过程以下列 4 种形式返回数据：

  • 输出参数，既可以返回数据（整型值或字符值等），也可以返回游标变量（游标是可以逐行检索的结果集）。有关使用游标变量的更多信息，请参见 Transact-SQL 游标名称的作用域。

  • 返回代码，始终是整型值。

  • SELECT 语句的结果集，这些语句包含在该存储过程内或该存储过程所调用的任何其它存储过程内。有关使用 SELECT 语句的更多信息，请参见查询基础知识。

  • 可从存储过程外引用的全局游标。有关使用游标变量的更多信息，请参见 Transact-SQL 游标名称的作用域。


使用 OUTPUT 参数返回数据

如果在过程定义中为参数指定 OUTPUT 关键字，则存储过程在退出时可将该参数的当前值返回至调用程序。若要用变量保存参数值以便在调用程序中使用，则调用程序必须在执行存储过程时使用 OUTPUT 关键字。

示例

下列示例显示有一个输入参数和一个输出参数的存储过程。存储过程中的第一个参数 @title 将接收由调用程序指定的输入值，而第二个参数 @ytd_sales 将向调用程序返回该值。SELECT 语句使用 @title 参数以获得正确的 ytd_sales 值，并将该值赋予 @ytd_sales 输出参数。

CREATE PROCEDURE get_sales_for_title
@title varchar(80),   -- This is the input parameter.
@ytd_sales int OUTPUT -- This is the output parameter.
AS

-- Get the sales for the specified title and
-- assign it to the output parameter.
SELECT @ytd_sales = ytd_sales
FROM titles
WHERE title = @title

RETURN
GO

下列程序用输入参数值执行存储过程，并将存储过程的输出值保存到调用程序的局部变量 @ytd_sales_for_title 中。

-- Declare the variable to receive the output value of the procedure.
DECLARE @ytd_sales_for_title int

-- Execute the procedure with a title_id value
-- and save the output value in a variable.

EXECUTE get_sales_for_title
"Sushi, Anyone?", @ytd_sales = @ytd_sales_for_title OUTPUT

-- Display the value returned by the procedure.
PRINT 'Sales for "Sushi, Anyone?": ' +    convert(varchar(6),@ytd_sales_for_title)
GO

Sales for "Sushi, Anyone?": 4095

执行存储过程时，也可为 OUTPUT 参数指定输入值。这样将允许存储过程从调用程序中接收一个值，更改该值或对该值执行操作，然后将新值返回至调用程序。在前面的示例中，可在执行存储过程前将一个值赋予 @ytd_sales_for_title 变量。@ytd_sales 变量在存储过程主体中包含参数值，而该存储过程在退出时，将 @ytd_sales 变量值返回至调用程序。这常常被称作"传址调用功能"。

如果在执行存储过程时对参数指定 OUTPUT，而在存储过程中该参数又不是用 OUTPUT 定义的，那么将收到一条错误信息。在执行带有 OUTPUT 参数的存储过程时，可以不指定 OUTPUT。这样不会返回错误，但将无法在调用程序中使用该输出值。

请参见

EXECUTE

Transact-SQL 游标名称的作用域


使用返回代码返回数据

存储过程可返回一个称为返回代码的整型值，以表明过程的执行状态。使用 RETURN 语句指定存储过程的返回代码。与 OUTPUT 参数一样，执行存储过程时必须将返回代码保存到变量中，以便在调用程序中使用返回代码值。例如，使用 int 数据类型的赋值变量 @result 保存存储过程 my_proc 的返回代码：

DECLARE @result int
EXECUTE @result = my_proc

返回代码通常用在存储过程内的控制流块中，以便为每种可能的错误状况设置返回代码值。可在 Transact-SQL 语句后使用 @@ERROR 函数，以检测是否在该语句的执行期间有错误发生。

示例
A. 根据错误类型返回不同的返回代码

下例显示带有特殊处理的过程 get_sales_for_title，该过程将为各种错误设置特殊的返回代码值。下表显示该存储过程向每种可能的错误所赋予的整型值。

值 | 含义
0 | 成功执行。
1 | 未指定所需的参数值。
2 | 指定的参数值无效。
3 | 获得销售值时出现错误。
4 | 发现销售值为 NULL 的书名。

CREATE PROCEDURE get_sales_for_title
-- This is the input parameter, with a default.
@title varchar(80) = NULL,
-- This is the output parameter.
@ytd_sales int OUTPUT
AS

-- Validate the @title parameter.
IF @title IS NULL
BEGIN
   PRINT "ERROR: You must specify a title value."
   RETURN(1)
END
ELSE
BEGIN
   -- Make sure the title is valid.
   IF (SELECT COUNT(*) FROM titles
      WHERE title = @title) = 0
      RETURN(2)
END

-- Get the sales for the specified title and
-- assign it to the output parameter.
SELECT @ytd_sales = ytd_sales
FROM titles
WHERE title = @title

-- Check for SQL Server errors.
IF @@ERROR <> 0
BEGIN
   RETURN(3)
END
ELSE
BEGIN
   -- Check to see if the ytd_sales value is NULL.
   IF @ytd_sales IS NULL
      RETURN(4)
   ELSE
      -- SUCCESS!!
      RETURN(0)
END

GO

以这种方法使用返回代码，将使调用程序得以检测和处理执行存储过程时发生的错误。

B. 处理从存储过程返回的不同返回代码

下例创建处理从 get_sales_for_title 过程返回的返回代码的程序。

-- Declare the variables to receive the output value and return code
-- of the procedure.
DECLARE @ytd_sales_for_title int, @ret_code INT

-- Execute the procedure with a title_id value
-- and save the output value and return code in variables.
EXECUTE @ret_code = get_sales_for_title
"Sushi, Anyone?",
@ytd_sales = @ytd_sales_for_title OUTPUT

--  Check the return codes.
IF @ret_code = 0
BEGIN
   PRINT "Procedure executed successfully"
   -- Display the value returned by the procedure.
   PRINT 'Sales for "Sushi, Anyone?": ' + CONVERT(varchar(6),@ytd_sales_for_title)
END
ELSE IF @ret_code = 1
   PRINT "ERROR: No title_id was specified."
ELSE IF @ret_code = 2
   PRINT "ERROR: An invalid title_id was specified."
ELSE IF @ret_code = 3
   PRINT "ERROR: An error occurred getting the ytd_sales."

GO


执行存储过程

当需要执行存储过程时，请使用 Transact-SQL EXECUTE 语句。如果存储过程是批处理中的第一条语句，那么不使用 EXECUTE 关键字也可以执行该存储过程。

如果存储过程编写为可以接受参数值，那么可以提供参数值。

说明 如果使用 @parameter = value 的形式，那么可以按任何顺序提供参数。还可以省略那些已提供默认值的参数。如果以 @parameter = value 形式提供了一个参数，就必须按此种形式提供后面所有的参数。如果没有以 @parameter = value 形式提供参数，则必须按照 CREATE PROCEDURE 语句中给出的顺序提供参数。

执行存储过程时，服务器将拒绝所有未包含在过程创建期间的参数列表中的参数。如果参数名称不匹配，用地址传递（显式传递该参数名称）的任何参数都不会被接受。

虽然可以省略已提供默认值的参数，但只能截断参数列表。例如，如果一个存储过程有五个参数，可以省略第四个和第五个参数，但不能跳过第四个参数而仍然包含第五个参数，除非以 @parameter = value 形式提供参数。

如果在存储过程中定义了参数的默认值，那么下列情况下将使用默认值：

  • 执行存储过程时未指定该参数的值。

  • 将 DEFAULT 关键字指定为该参数的值。

若要执行与其它同名存储过程处于同一分组中的存储过程，请指定此存储过程在组内的标识号。例如，要执行 my_proc 组中的第二个存储过程，请执行：

EXECUTE my_proc;2

执行存储过程

Transact-SQL

请参见

批处理

扩展存储过程的执行特点

SQL 事件探查器方案

指定参数

调试扩展存储过程

SQL 存储过程

使用返回代码返回数据

重新编译存储过程


自动执行存储过程

如果将存储过程标记为自动执行，则每次启动 Microsoft® SQL Server™ 2000 时都会执行这些存储过程。

如果有需要定期执行的操作，或者有作为后台进程运行的存储过程，并希望该存储过程在所有时间都处于运行状态，此种方法非常有用。自动执行存储过程的另一个用途是使该存储过程完成 tempdb 中的系统或维护任务，如创建一个全局临时表。这将确保在 SQL Server 启动并重新创建 tempdb 时，始终存在这样一个临时表。

自动执行的存储过程使用与固定服务器角色 sysadmin 成员相同的权限进行操作。该存储过程生成的所有错误信息都将写入 SQL Server 错误日志中。请勿从自动执行的存储过程中返回任何结果集。因为该存储过程由 SQL Server 而不是某位用户执行，所以结果集将无处可去。

虽然设置存储过程的自动执行状态是个别进行的，但可以设置 SQL Server 的 scan for startup procs 配置选项以防止所有的存储过程在启动 SQL Server 时自动执行。

设置或撤消自动执行的存储过程

Transact-SQL

SQL-DMO

设置或撤消 scan for startup procs 配置选项

Transact-SQL

请参见

scan for startup procs 选项


修改和重命名存储过程

如果需要更改存储过程中的语句或参数，可以删除和重新创建该存储过程，也可以用单个步骤更改该存储过程。删除和重新创建存储过程时，所有与该存储过程相关联的权限都将丢失。更改存储过程时，过程或参数定义会更改，但为该存储过程定义的权限将保留。

也可以重命名存储过程。新名称必须遵守标识符规则。您只能重命名自己拥有的存储过程，但数据库所有者可以更改任何用户的存储过程名称。要重命名的存储过程必须位于当前数据库中。

还可以修改存储过程以加密其定义或使该过程在每次执行时都得到重新编译。

说明 对存储过程名称或定义的更改可能导致所有相关对象在执行时失败，如果这些相关对象未进行更新以反映对该存储过程所做的更改。

修改存储过程

Transact-SQL

企业管理器

企业管理器

SQL-DMO

重命名存储过程

Transact-SQL

企业管理器

企业管理器

SQL-DMO

说明 重命名存储过程不会更改该过程在其定义文本中的名称。若要更改定义中的存储过程名称，请直接修改该存储过程。

注意 不应重命名扩展存储过程，除非用于定义该扩展存储过程的代码也进行更改。否则，将不能执行该存储过程。

请参见

延迟名称解析和编译


重新编译存储过程

在用诸如添加索引或更改索引列中的数据等操作更改数据库时，应通过重新编译访问数据库表的原始查询计划使其得到重新优化。在重新启动 Microsoft® SQL Server™ 2000 后第一次运行存储过程时自动发生该优化。当存储过程使用的基础表更改时也会发生优化。但是如果添加了存储过程可能从中受益的新索引，将不发生自动优化，直到下一次 SQL Server 重新启动后再运行该存储过程时为止。

SQL Server 提供三种重新编译存储过程的方法：

  • sp_recompile 系统存储过程强制在下次运行存储过程时进行重新编译。

  • 创建存储过程时在其定义中指定 WITH RECOMPILE 选项，表明 SQL Server 将不对该存储过程计划进行高速缓存；该存储过程将在每次执行时都重新编译。当存储过程的参数值在各次执行间都有较大差异，导致每次均需创建不同的执行计划时，可使用 WITH RECOMPILE 选项。此选项并不常用，因为每次执行存储过程时都必须对其进行重新编译，这样会使存储过程的执行变慢。

  • 在执行存储过程时指定 WITH RECOMPILE 选项，可强制对存储过程进行重新编译。仅当所提供的参数不典型，或者自创建该存储过程后数据发生显著更改时才应使用此选项。

说明 如果删除或重命名了存储过程所引用的对象，那么在执行该存储过程时会返回错误。但如果用同名对象替换了存储过程中引用的对象，则可以不进行重新编译而继续执行该存储过程。

下次运行时重新编译存储过程

Transact-SQL

请参见

创建存储过程

延迟名称解析和编译

执行存储过程

设计存储过程


查看存储过程

几个系统存储过程用系统表提供有关存储过程的信息。使用这些存储过程可以：

  • 查看用于创建存储过程的 Transact-SQL 语句。这对于没有用于创建存储过程的 Transact-SQL 脚本文件的用户是很有用的。

  • 获得有关存储过程的信息（如存储过程的所有者、创建时间及其参数）。

  • 列出指定存储过程所使用的对象及使用指定存储过程的过程。此信息可用来识别那些受数据库中某个对象的更改或删除影响的过程。

查看存储过程的定义

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看有关存储过程的信息

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看存储过程的相关性

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看关于扩展存储过程的信息

Transact-SQL

企业管理器

企业管理器

请参见

查询安装在 SQL Server 中的扩展存储过程


删除存储过程

不再需要存储过程时可将其删除。如果另一个存储过程调用某个已删除的存储过程，则 Microsoft® SQL Server™ 2000 会在执行该调用过程时显示一条错误信息。但如果定义了同名和参数相同的新存储过程来替换已删除存储过程，那么引用该过程的其它过程仍能顺利执行。例如，如果存储过程 proc1 引用存储过程 proc2，而 proc2 被删除，但由创建了另一个名为 proc2 的存储过程，现在 proc1 将引用这一新存储过程，proc1 也不必重新编译。

存储过程分组后，将无法删除组内的单个存储过程。删除一个存储过程会将同一组内的所有存储过程都删除。

删除存储过程

Transact-SQL

企业管理器

企业管理器

SQL-DMO

删除扩展存储过程

Transact-SQL

企业管理器

企业管理器

请参见

延迟名称解析和编译

从 SQL Server 中删除扩展存储过程


用触发器强制执行业务规则

Microsoft® SQL Server™ 2000 提供了两种主要机制来强制业务规则和数据完整性：约束和触发器。触发器是一种特殊类型的存储过程，它在指定的表中的数据发生变化时自动生效。唤醒调用触发器以响应 INSERT、UPDATE 或 DELETE 语句。触发器可以查询其它表，并可以包含复杂的 Transact-SQL 语句。将触发器和触发它的语句作为可在触发器内回滚的单个事务对待。如果检测到严重错误（例如，磁盘空间不足），则整个事务即自动回滚。

触发器的优点如下：

  • 触发器可通过数据库中的相关表实现级联更改；不过，通过级联引用完整性约束可以更有效地执行这些更改。

  • 触发器可以强制比用 CHECK 约束定义的约束更为复杂的约束。

    与 CHECK 约束不同，触发器可以引用其它表中的列。例如，触发器可以使用另一个表中的 SELECT 比较插入或更新的数据，以及执行其它操作，如修改数据或显示用户定义错误信息。

  • 触发器也可以评估数据修改前后的表状态，并根据其差异采取对策。

  • 一个表中的多个同类触发器（INSERT、UPDATE 或 DELETE）允许采取多个不同的对策以响应同一个修改语句。
比较触发器与约束

约束和触发器在特殊情况下各有优势。触发器的主要好处在于它们可以包含使用 Transact-SQL 代码的复杂处理逻辑。因此，触发器可以支持约束的所有功能；但它在所给出的功能上并不总是最好的方法。

实体完整性总应在最低级别上通过索引进行强制，这些索引或是 PRIMARY KEY 和 UNIQUE 约束的一部分，或是在约束之外独立创建的。假设功能可以满足应用程序的功能需求，域完整性应通过 CHECK 约束进行强制，而引用完整性 (RI) 则应通过 FOREIGN KEY 约束进行强制。

在约束所支持的功能无法满足应用程序的功能要求时，触发器就极为有用。例如：

  • 除非 REFERENCES 子句定义了级联引用操作，否则 FOREIGN KEY 约束只能以与另一列中的值完全匹配的值来验证列值。

  • CHECK 约束只能根据逻辑表达式或同一表中的另一列来验证列值。如果应用程序要求根据另一个表中的列验证列值，则必须使用触发器。

  • 约束只能通过标准的系统错误信息传递错误信息。如果应用程序要求使用（或能从中获益）自定义信息和较为复杂的错误处理，则必须使用触发器。

触发器可通过数据库中的相关表实现级联更改；不过，通过级联引用完整性约束可以更有效地执行这些更改。

  • 触发器可以禁止或回滚违反引用完整性的更改，从而取消所尝试的数据修改。当更改外键且新值与主键不匹配时，此类触发器就可能发生作用。例如，可以在 titleauthor.title_id 上创建一个插入触发器，使它在新值与 titles.title_id 中的某个值不匹配时回滚一个插入。不过，通常使用 FOREIGN KEY 来达到这个目的。

  • 如果触发器表上存在约束，则在 INSTEAD OF 触发器执行后但在 AFTER 触发器执行前检查这些约束。如果约束破坏，则回滚 INSTEAD OF 触发器操作并且不执行 AFTER 触发器。

请参见

级联引用完整性约束

CHECK 约束

数据完整性

存储过程

使用约束、默认值和空值


设计触发器

在设计触发器时，Microsoft® SQL Server™ 2000 提供了两种选项：

  • 执行 INSTEAD OF 触发器代替通常的触发动作。INSTEAD OF 触发器还可在带有一个或多个基表的视图上定义，而在这些视图上这些触发器可扩展视图可支持的更新类型。

  • 在执行了 INSERT、UPDATE 或 DELETE 语句操作之后执行 AFTER 触发器。指定 AFTER 与指定 FOR 相同，而后者是 SQL Server 早期版本中唯一可使用的选项。AFTER 触发器只能在表上指定。

该表比较 AFTER 触发器和 INSTEAD OF 触发器的功能。

功能 | AFTER 触发器 | INSTEAD OF 触发器
适用范围 | 表 | 表和视图
每个表或视图含触发器数量 | ()每个触发动作（UPDATE、DELETE 和 INSERT）含多个触发器 | 每个触发动作（UPDATE、DELETE 和 INSERT）含一个触发器
级联引用 | 不应用任何限制 | 在作为级联引用完整性约束目标的表上限制应用。
执行 晚于：
  • 约束处理

  • 声明引用操作

  • inserted 和 deleted 表的创建

  • 触发动作
 早于:
  • 约束处理

代替：

  • 触发动作

晚于：

  • inserted 和 deleted 表的创建
执行顺序 | 可指定第一个和最后一个执行 | 不可用
在 inserted 和 deleted 表中引用 text、ntext 和 image 列 | 不允许 | 允许

请参见

使用 inserted 和 deleted 表


指定触发器何时激发

可指定以下两个选项之一来控制触发器何时激发：

  • AFTER 触发器在触发操作（INSERT、UPDATE 或 DELETE）后和处理完任何约束后激发。可通过指定 AFTER 或 FOR 关键字来请求 AFTER 触发器。因为 FOR 关键字与 AFTER 的效果相同，所以具有 FOR 关键字的触发器也归类为 AFTER 触发器。

  • INSTEAD OF 触发器代替触发动作进行激发，并在处理约束之前激发。

对于每个触发操作（UPDATE、DELETE 和 INSERT），每个表或视图只能有一个 INSTEAD OF 触发器。而一个表对于每个触发操作可以有多个 AFTER 触发器。

示例
A. 用 INSTEAD OF 触发器代替标准触发动作
CREATE TRIGGER TableAInsertTrig ON TableA
INSTEAD OF INSERT
AS ...
B. 用 AFTER 触发器增加标准触发动作
CREATE TRIGGER TableBDeleteTrig ON TableB
AFTER DELETE
AS ...
C. 用 FOR 触发器增加标准触发动作
-- This statement uses the FOR keyword to generate an AFTER trigger.
CREATE TRIGGER TableCUpdateTrig ON TableC
FOR UPDATE
AS ...


触发器执行

如果发生约束侵犯，则永远不会执行 AFTER 触发器，因此这些触发器不能用于任何可能防止约束侵犯的处理。

执行 INSTEAD OF 触发器而不是通常的触发动作。在创建将更改反映到基表的 inserted 和 deleted 表之后，但在执行任何操作之前执行这些触发器。这些触发器在执行任何约束前执行，因此可执行预处理以补充约束操作。

如果某个表上定义的 INSTEAD OF 触发器对该表执行一个通常将再次激发 INSTEAD OF 触发器的语句，则将不递归调用该触发器。相反，将视该表为没有 INSTEAD OF 触发器来处理该语句，并启动约束操作与 AFTER 触发器执行之间的链。例如，如果将触发器定义为一个表的 INSTEAD OF INSERT 触发器，并且该触发器在同一表上执行 INSERT 语句，则由 INSTEAD OF 触发器执行的 INSERT 语句不会再次调用该触发器。该触发器执行的 INSERT 将启动这样的进程，执行约束操作并激发为表定义的任何 AFTER INSERT 触发器。

如果视图上定义的 INSTEAD OF 触发器对该视图执行一个通常将再次激发 INSTEAD OF 触发器的语句，则将不递归调用该触发器。而是将该语句解析为对视图的基础基表的更改。在这种情况下，视图定义必须满足对可更新视图的所有限制。有关可更新视图的定义，请参见通过视图修改数据。例如，如果将触发器定义为某个视图的 INSTEAD OF UPDATE 触发器，并且该触发器执行引用该视图的 UPDATE 语句，则由 INSTEAD OF 触发器执行的 UPDATE 语句不会再次调用该触发器。在处理该触发器对视图执行的 UPDATE 语句时，将视图看作没有 INSTEAD OF 触发器一样对待。UPDATE 更改的列必须解析到单个基表。对基础基表所做的每个修改将启动应用约束和激发为表定义的 AFTER 触发器之间的链。

触发器的性能开销通常很低。运行触发器的时间大部分都用于引用其它表，这些表可以在内存或数据库设备上。Deleted 和 inserted 表总是在内存中。触发器所引用的其它表的位置确定操作所需的时间。

说明 由于存在对性能的潜在负面影响，不推荐在触发器中使用游标。使用基于行集的逻辑而非游标来设计影响多行的触发器。

请参见

使用 inserted 和 deleted 表


设计 INSTEAD OF 触发器

INSTEAD OF 触发器的主要优点是可以使不能更新的视图支持更新。包含多个基表的视图必须使用 INSTEAD OF 触发器来支持引用表中数据的插入、更新和删除操作。INSTEAD OF 触发器的另一个优点是使您得以编写这样的逻辑代码：可以拒绝批处理中的某些部分同时允许批处理的其它部分成功。

INSTEAD OF 触发器可以进行以下操作：

  • 忽略批处理中的某些部分。

  • 不处理批处理中的某些部分并记录有问题的行。

  • 如果遇到错误情况则采取备用操作。

说明 在含有用 DELETE 或 UPDATE 操作定义的外键的表上，不能定义 INSTEAD OF DELETE 和 INSTEAD OF UPDATE 触发器。

将此逻辑作为 INSTEAD OF 触发器的一部分进行编码，可避免所有访问数据的应用程序必须重新执行该逻辑。

在下列 Transact-SQL 语句序列中，INSTEAD OF 触发器更新视图中的两个基表。另外，显示两种处理错误的方法：

  • 忽略对 Person 表的重复插入，并且插入的信息将记录在 PersonDuplicates 表中。

  • 将对 EmployeeTable 表的重复插入转变为 UPDATE 语句，该语句将当前信息检索至 EmployeeTable，而不会产生重复键侵犯。

Transact-SQL 语句创建两个基表、一个视图、一个记录错误表和视图上的 INSTEAD OF 触发器。下面的这些表将个人数据和业务数据分开并且是视图的基表：

CREATE TABLE Person
   (
    SSN         char(11) PRIMARY KEY,
    Name        nvarchar(100),
    Address     nvarchar(100),
    Birthdate   datetime
   )

CREATE TABLE EmployeeTable
   (
    EmployeeID       int PRIMARY KEY,
    SSN              char(11) UNIQUE,
    Department       nvarchar(10),
    Salary           money,
    CONSTRAINT FKEmpPer FOREIGN KEY (SSN)
    REFERENCES Person (SSN)
   )

下面的视图使用某个人的两个表中的所有相关数据建立报表：

CREATE VIEW Employee AS
SELECT P.SSN as SSN, Name, Address,
       Birthdate, EmployeeID, Department, Salary
FROM Person P, EmployeeTable E
WHERE P.SSN = E.SSN

可记录对插入具有重复的社会安全号的行的尝试。PersonDuplicates 表记录插入的值、尝试插入操作的用户的用户名和插入的时间：

CREATE TABLE PersonDuplicates
   (
    SSN           char(11),
    Name          nvarchar(100),
    Address       nvarchar(100),
    Birthdate     datetime,
    InsertSNAME   nchar(100),
    WhenInserted  datetime
   )

INSTEAD OF 触发器在单独视图的多个基表中插入行。将对插入具有重复社会安全号的行的尝试记录在 PersonDuplicates 表中。将 EmployeeTable 中的重复行更改为更新语句。

CREATE TRIGGER IO_Trig_INS_Employee ON Employee
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON
-- Check for duplicate Person. If no duplicate, do an insert.
IF (NOT EXISTS (SELECT P.SSN
      FROM Person P, inserted I
      WHERE P.SSN = I.SSN))
   INSERT INTO Person
      SELECT SSN,Name,Address,Birthdate,Comment
      FROM inserted
ELSE
-- Log attempt to insert duplicate Person row in PersonDuplicates table.
   INSERT INTO PersonDuplicates
      SELECT SSN,Name,Address,Birthdate,SUSER_SNAME(),GETDATE()
      FROM inserted
-- Check for duplicate Employee. If no duplicate, do an insert.
IF (NOT EXISTS (SELECT E.SSN
      FROM EmployeeTable E, inserted
      WHERE E.SSN = inserted.SSN))
   INSERT INTO EmployeeTable
      SELECT EmployeeID,SSN, Department, Salary,Comment
      FROM inserted
ELSE
--If duplicate, change to UPDATE so that there will not
--be a duplicate key violation error.
   UPDATE EmployeeTable
      SET EmployeeID = I.EmployeeID,
          Department = I.Department,
          Salary = I.Salary,
          Comment = I.Comment
   FROM EmployeeTable E, inserted I
   WHERE E.SSN = I.SSN
END


创建触发器

创建触发器前应考虑下列问题：

  • CREATE TRIGGER 语句必须是批处理中的第一个语句。将该批处理中随后的其它所有语句解释为 CREATE TRIGGER 语句定义的一部分。

  • 创建触发器的权限默认分配给表的所有者，且不能将该权限转给其他用户。

  • 触发器为数据库对象，其名称必须遵循标识符的命名规则。

  • 虽然触发器可以引用当前数据库以外的对象，但只能在当前数据库中创建触发器。

  • 虽然不能在临时表或系统表上创建触发器，但是触发器可以引用临时表。不应引用系统表，而应使用信息架构视图。有关更多信息，请参见信息架构视图。

  • 在含有用 DELETE 或 UPDATE 操作定义的外键的表中，不能定义 INSTEAD OF 和 INSTEAD OF UPDATE 触发器。

  • 虽然 TRUNCATE TABLE 语句类似于没有 WHERE 子句（用于删除行）的 DELETE 语句，但它并不会引发 DELETE 触发器，因为 TRUNCATE TABLE 语句没有记录。

  • WRITETEXT 语句不会引发 INSERT 或 UPDATE 触发器。

创建触发器时需指定：

  • 名称。

  • 在其上定义触发器的表。

  • 触发器将何时激发。

  • 激活触发器的数据修改语句。有效选项为 INSERT、UPDATE 或 DELETE。多个数据修改语句可激活同一个触发器。例如，触发器可由 INSERT 或 UPDATE 语句激活。

  • 执行触发操作的编程语句。
多个触发器

一个表中可有同类型的多个 AFTER 触发器，前提条件是它们的名称各不相同；每个触发器可以执行多个函数。但是，每个触发器只能应用于一个表，而单个触发器可应用于三个用户操作（UPDATE、INSERT 和 DELETE）的任何子集。

一个表只能有一个给定类型的 INSTEAD OF 触发器。

触发器权限和所有权

CREATE TRIGGER 权限默认授予定义触发器的表所有者、sysadmin 固定服务器角色成员以及 db_owner 和 db_ddladmin 固定数据库角色成员，并且不可转让。

如果在某个视图上创建 INSTEAD OF 触发器，则所有关系链将断开（如果视图所有者不同时拥有视图和触发器所引用的基表）。对于不属于视图所有者的基表，表所有者必须单独地将必要的权限授予正阅读或更新该视图的任何人。如果同一用户拥有视图和基础基表，则视图和基础基表必须授予其他用户视图的权限，而非个别基表的权限。有关更多信息，请参见使用所有权链。

创建触发器

Transact-SQL

企业管理器

企业管理器

SQL-DMO


设计触发器

几乎所有可以写成批处理的 Transact-SQL 语句都可以用来创建触发器，下列情况除外：

ALTER DATABASE | CREATE DATABASE | DISK INIT
DISK RESIZE | DROP DATABASE | LOAD DATABASE
LOAD LOG | RECONFIGURE | RESTORE DATABASE
RESTORE LOG |  |

重要 在 Microsoft® SQL Server 2000™ 中包括 DISK RESIZE、DISK INIT、LOAD DATABASE 和 LOAD LOG 语句只是为了向后兼容，以后可能不再支持这些语句。

对触发器定义进行加密

若想确保其他用户不能查看触发器定义，请使用 WITH ENCRYPTION 子句。触发器定义即以无法读取的格式进行存储。

一旦进行加密，触发器的定义即不能再行解密，任何人都无法进行查看，包括触发器的所有者和系统管理员。

SET 语句选项

当 ODBC 应用程序与 SQL Server 连接时，服务器将自动设置会话的下列选项：

  • SET QUOTED_IDENTIFIER ON

  • SET TEXTSIZE 2147483647

  • SET ANSI_DEFAULTS ON

  • SET CURSOR_CLOSE_ON_COMMIT OFF

  • SET IMPLICIT_TRANSACTIONS OFF

这些设置将提高 ODBC 应用程序的可移植性。由于基于 DB-Library– 的应用程序通常不设置这些选项，触发器需使用上述 SET 选项（ON 和 OFF 状态下）进行测试。这可以确保触发器正常工作，而无论特定连接在唤醒调用触发器时设置的选项如何。如果触发器需要为这些选项之一进行特殊设置，它必须在触发器初始阶段发出一个 SET 语句。该 SET 语句仅在触发器执行期间有效，触发器完成时即恢复原始设置。

对特定列的更改进行测试

触发器定义中的 IF UPDATE (列名)子句可用来确定 INSERT 或 UPDATE 语句是否影响到表中的一个特定列。无论何时，当列被赋值时，该子句即为 TRUE。

说明 由于列中的特定值无法使用 DELETE 语句删除，IF UPDATE 子句也就不能应用于 DELETE 语句。

或者可以用 IF COLUMNS_UPDATED() 子句检查表中已用 INSERT 或 UPDATE 语句更新的列。该子句使用整型位掩码指定需测试的列。有关更多信息，请参见 CREATE TRIGGER。

示例
A. 使用 IF UPDATE 子句测试数据修改

下例在表 my_table 中创建名为 my_trig 的 INSERT 触发器，并测试列 b 是否受到任何 INSERT 语句的影响。

CREATE TABLE my_table*
(a int NULL, b int NULL)
GO

CREATE TRIGGER my_trig
ON my_table
FOR INSERT
AS
IF UPDATE(b)
   PRINT 'Column b Modified'
GO
B. 使用 COLUMNS UPDATED() 子句测试数据修改

下例使用 COLUMNS_UPDATED() 子句可以获得相似的结果。

CREATE TRIGGER my_trig2
ON my_table
FOR INSERT
AS
IF ( COLUMNS_UPDATED() & 2 = 2 )
   PRINT 'Column b Modified'
GO
延迟名称解析

触发器可以引用触发器创建时不存在的表。这称为延迟名称解析。有关延迟名称解析的更多信息，请参见延迟名称解析和编译。

说明 如果触发器引用的一个对象被删除或重命名，则执行触发器时会返回错误。但是，如果用同一名称的对象替代触发器中所引用的对象，则无须重新创建即可执行触发器。例如，如果触发器 trig1 引用表 test1，在删除该 test1 表后，创建一个同样名为 test1 的不同的表，则 trig1 在执行时会引用新表。

返回结果

建议不要让触发器返回任何结果。这是因为对这些返回结果的特殊处理必须写入每个允许对触发器表进行修改的应用程序中。为了阻止从触发器返回任何结果，不要在触发器定义中包含 SELECT 语句或变量赋值。如果必须在触发器中进行变量赋值，则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。

请参见

CREATE TRIGGER

SELECT

SET


使用包含 ROLLBACK TRANSACTION 的触发器

当包含 ROLLBACK TRANSACTION 语句的触发器从批处理中执行时，它们会取消整个批处理。在下面的示例中，如果 INSERT 语句引发了包含 ROLLBACK TRANSACTION 的触发器，则由于批处理被取消，DELETE 语句没有执行：

/* Start of Batch */
INSERT employee VALUES ('XYZ12345M', 'New', 'M', 'Employee', 1, 1, '9952', '6/1/95') -- Causes trigger to fire and ROLLBACK TRANSACTION.
DELETE employee WHERE emp_id = 'PMA42628M'
GO

如果包含 ROLLBACK TRANSACTION 语句的触发器从用户定义的事务中引发，则 ROLLBACK TRANSACTION 回滚整个事务。在本示例中，如果 INSERT 语句引发了包含 ROLLBACK TRANSACTION 的触发器，UPDATE 语句同样被回滚：

/* Start of Transaction */
BEGIN TRANSACTION
UPDATE employee SET hire_date = '7/1/94' WHERE emp_id = 'VPA30890F'
INSERT employee VALUES ('XYZ12345M', 'New', 'M', 'Employee', 1, 1, '9952', '6/1/95') -- Causes trigger to fire and ROLLBACK TRANSACTION

请参见

在存储过程和触发器中回滚

ROLLBACK TRANSACTION

事务


使用 inserted 和 deleted 表

触发器语句中使用了两种特殊的表：deleted 表和 inserted 表。Microsoft® SQL Server™ 2000 自动创建和管理这些表。可以使用这两个临时的驻留内存的表测试某些数据修改的效果及设置触发器操作的条件；然而，不能直接对表中的数据进行更改。

inserted 和 deleted 表主要用于触发器中：

  • 扩展表间引用完整性。

  • 在以视图为基础的基表中插入或更新数据。

  • 检查错误并基于错误采取行动。

  • 找到数据修改前后表状态的差异，并基于此差异采取行动。

Deleted 表用于存储 DELETE 和 UPDATE 语句所影响的行的复本。在执行 DELETE 或 UPDATE 语句时，行从触发器表中删除，并传输到 deleted 表中。Deleted 表和触发器表通常没有相同的行。

Inserted 表用于存储 INSERT 和 UPDATE 语句所影响的行的副本。在一个插入或更新事务处理中，新建行被同时添加到 inserted 表和触发器表中。Inserted 表中的行是触发器表中新行的副本。

更新事务类似于在删除之后执行插入；首先旧行被复制到 deleted 表中，然后新行被复制到触发器表和 inserted 表中。

在设置触发器条件时，应当为引发触发器的操作恰当使用 inserted 和 deleted 表。虽然在测试 INSERT 时引用 deleted 表或在测试 DELETE 时引用 inserted 表不会引起任何错误，但是在这种情形下这些触发器测试表中不会包含任何行。

说明 如果触发器操作取决于一个数据修改所影响的行数，应该为多行数据修改（基于 SELECT 语句的 INSERT、DELETE 或 UPDATE）使用测试（如检查 @@ROWCOUNT），然后采取相应的对策。

SQL Server™ 2000 不允许 AFTER 触发器引用 inserted 和 deleted 表中的 text、ntext 或 image 列；然而，允许 INSTEAD OF 触发器引用这些列。有关更多信息，请参见 CREATE TRIGGER。

在 INSTEAD OF 触发器中使用 inserted 和 deleted 表

传递到在表上定义的 INSTEAD OF 触发器的 inserted 和 deleted 表遵从与传递到 AFTER 触发器的 inserted 和 deleted 表相同的规则。inserted 和 deleted 表的格式与在其上定义 INSTEAD OF 触发器的表的格式相同。inserted 和 deleted 表中的每一列都直接映射到基表中的列。

  • 有关引用带 INSTEAD OF 触发器的表的 INSERT 或 UPDATE 语句何时必须提供列值的规则与表没有 INSTEAD OF 触发器时相同：
    • 不能为计算列或具有 timestamp 数据类型的列指定值。
      1. 不能为具有 IDENTITY 属性的列指定值，除非该列的 IDENTITY_INSERT 为 ON。当 IDENTITY_INSERT 为 ON 时，INSERT 语句必须提供一个值。
    • INSERT 语句必须为所有无 DEFAULT 约束的 NOT NULL 列提供值。

    • 对于除计算列、标识列或 timestamp 列以外的任何列，任何允许空值的列或具有 DEFAULT 定义的 NOT NULL 列的值都是可选的。

当 INSERT、UPDATE 或 DELETE 语句引用具有 INSTEAD OF 触发器的视图时，数据库引擎将调用该触发器，而不是对任何表采取任何直接操作。即使为视图生成的 inserted 和 deleted 表中的信息格式与基表中的数据格式不同，该触发器在生成执行基表中的请求操作所需的任何语句时，仍必须使用 inserted 和 deleted 表中的信息。

传递到在视图上定义的 INSTEAD OF 触发器的 inserted 和 deleted 表格式与为该视图定义的 SELECT 语句的选择列表相匹配。例如：

CREATE VIEW EmployeeNames (EmployeeID, LName, FName)
AS
SELECT EmployeeID, LastName, FirstName
FROM Northwind.dbo.Employees

视图的结果集有三列：一个 int 列和两个 nvarchar 列。传递到在视图上定义的 INSTEAD OF 触发器的 inserted 和 deleted 表也具有名为 EmployeeID 的 int 列、名为 LName 的 nvarchar 列和名为 FName 的 nvarchar 列。

视图的选择列表还包含不直接映射到单个基表列的表达式。一些视图表达式（如常量调用或函数调用）可能不引用任何列，这类表达式会被忽略。复杂的表达式会引用多列，但在 inserted 和 deleted 表中，每个插入的行仅有一个值。如果视图中的简单表达式引用具有复杂表达式的计算列，则这些简单表达式也有同样的问题。视图上的 INSTEAD OF 触发器必须处理这些类型的表达式。有关更多信息，请参见视图上 INSTEAD OF 触发器中的表达式和计算列。


关于多行的考虑事项

在写触发器代码时需要考虑的一个重要问题就是，引发触发器的语句可以是一个影响多行的单一语句，而不仅是影响一行。这在 UPDATE 和 DELETE 触发器中很常见，因为这些语句经常作用于多行。而这在 INSERT 触发器中就比较少见，因为基本的 INSERT 语句只添加一行。然而，由于 INSERT 触发器可由 INSERT INTO (table_name) SELECT 语句激发，所以插入许多行可能导致单个的触发器调用。

在下列情况下关于多行的考虑尤为重要：触发器的功能是自动重新计算表中的汇总值，并将结果存储在另一个正在进行的计数中。

说明 由于存在对性能的潜在负面影响，不推荐在触发器中使用游标。使用基于行集的逻辑而非游标来设计影响多行的触发器。

示例

下例中触发器的目的是在另一个表中存储某列的运行总计。

A. 存储单行插入的运行总计

第一种触发器在一行数据装载入 sales 表中时能很好地进行单行插入。INSERT 语句激发触发器，新行在触发器执行期间装载在 inserted 表中。UPDATE 语句读取该行的 qty 列值，并将其添加到 titles 表的 ytd_sales 列中已有的值上。WHERE 子句则确保已在 sales 表中更新的行与 inserted 表中行的 title_id 相匹配。

-- Trigger is valid for single-row inserts.
CREATE TRIGGER intrig
ON sales
AFTER INSERT AS

   UPDATE titles
   SET ytd_sales = ytd_sales + qty
   FROM inserted
   WHERE titles.title_id = inserted.title_id
B. 存储多行或单行插入的运行总计

如果要进行多行插入，示例 A 中的触发器可能就不会正确工作；位于 UPDATE 语句 (ytd_sales + qty) 中赋值表达式右边的表达式只能是一个值，而不能是一个值列表。因此该触发器的作用就是获取 inserted 表中任意一行的值，并将其添加到 titles 表中特定 title_id 值的已有 ytd_sales 值上。如果某个 title_id 值在 inserted 表中出现了多次，则可能无法得到预期的效果。

为了正确地更新 titles 表，触发器就必须适应 inserted 表中出现多行的可能性。这可以通过 SUM 函数实现，它为 inserted 表中每个 title_id 计算一组行的总计 qty。SUM 函数存放于相关子查询中（SELECT 语句在括号内）。该子查询为 inserted 表中与 titles 表的 title_id 匹配或相关的每个 title_id 返回一个单一值。

-- Trigger is valid for multirow and single-row inserts.
CREATE TRIGGER intrig
ON sales
AFTER INSERT AS

   UPDATE titles
   SET ytd_sales = ytd_sales +
      (SELECT SUM(qty) -- Correlated subquery.
      FROM inserted
      WHERE titles.title_id = inserted.title_id)
   WHERE titles.title_id IN
      (SELECT title_id FROM inserted)

该触发器对单行插入同样适用；qty 值列的总计为单行的总计。不过，使用该触发器时，WHERE 子句中所使用的相关子查询和 IN 运算符需要 Microsoft® SQL Server™ 2000 的额外处理，而这对于单行插入来说是不必的。

C. 存储基于插入类型的运行总计

可以更改触发器以针对不同行数使用最优方法。例如，可以在触发器逻辑中使用 @@ROWCOUNT 函数以区分单行插入和多行插入。

-- Trigger valid for multirow and single row inserts
-- and optimal for single row inserts.
CREATE TRIGGER intrig
ON sales
FOR INSERT AS
IF @@ROWCOUNT = 1
BEGIN
   UPDATE titles
   SET ytd_sales = ytd_sales + qty
   FROM inserted
   WHERE titles.title_id = inserted.title_id
END
ELSE
BEGIN
   UPDATE titles
   SET ytd_sales = ytd_sales +
   (SELECT SUM(qty)
      FROM inserted
      WHERE titles.title_id = inserted.title_id)
   WHERE titles.title_id IN
      (SELECT title_id FROM inserted)
END


条件 INSERT 触发器

触发器在拒绝或接受每个数据修改事务时将其作为一个整体。不过，不必简单地只因为某些数据修改不可接受而回滚所有的数据修改。在触发器中使用相关子查询可以强制触发器逐个检查所修改的行。

示例
A. 使用 AFTER INSERT 触发器

下列示例假设在 pubs 数据库中存在一个名为 newsale 的表。以下是 newsale 的 CREATE 语句：

CREATE TABLE newsale
   (stor_id char(4),
   ord_num varchar(20),
   date datetime,
   qty smallint,
   payterms varchar(12),
   title_id tid)

如果希望检查正在尝试插入的每条记录，conditionalinsert 触发器会逐行对插入进行分析，然后删除在 titles 表中没有 title_id 的行。

CREATE TRIGGER conditionalinsert
ON sales
AFTER INSERT AS
IF
(SELECT COUNT(*) FROM titles, inserted
WHERE titles.title_id = inserted.title_id) <> @@ROWCOUNT
BEGIN
   DELETE sales FROM sales, inserted
   WHERE sales.title_id = inserted.title_id AND
      inserted.title_id NOT IN
         (SELECT title_id
         FROM titles)
   PRINT 'Only sales records with matching title_ids added.'
END

当插入了不可接受的标题时，事务并不回滚；相反，触发器会删除不需要的行。这种删除已插入行的能力取决于引发触发器时处理发生的先后顺序。首先，将行插入 sales 表和 inserted 表中，然后激发触发器。

如果要对触发器进行测试，在 newsale 表中插入四行。其中两行的 title_ids 与 titles 表中已有的任何 title_ids 均不匹配。

newsale

stor_id | ord_num | 日期 | qty | payterms | title_id
------- | -------- | ------------------- | --- | -------- | --------
7066 | QA7442.3 | Jul 25 1995 8:35AM | 75 | Net 30 | PS1372
7066 | QA7442.3 | Jul 24 1995 8:35AM | 75 | Net 60 | BU7832
7067 | D4482 | Jul 27 1995 12:00AM | 10 | Net 30 | PSxxxx
7131 | N914008 | Jul 27 1995 12:00AM | 20 | Net 30 | PSyyyy

第二步，将 newsale 中的数据插入 sales 表中。语句是这样的：

INSERT sales
SELECT * FROM newsale

Title_ids PSxxxx 和 PSyyyy 在 titles 表中没有匹配项，conditionalinsert 触发器即从 sales 和 inserted 表中将它们删除。


指定第一个和最后一个触发器

可将与表相关联的 AFTER 触发器之一指定为每个 INSERT、DELETE 和 UPDATE 触发动作执行的第一个或最后一个 AFTER 触发器。在第一个和最后一个触发器之间激发的 AFTER 触发器将按未定义的顺序执行。

若要指定 AFTER 触发器的顺序，请使用 sp_settriggerorder 存储过程。可用的选项有：

  • 第一个

    指定该触发器是为触发操作激发的第一个 AFTER 触发器。

  • 最后一个

    指定该触发器是为触发操作激发的最后一个 AFTER 触发器。

  • 无

    指定触发器的激发没有特定的顺序。主要用于重新设置第一个或最后一个触发器。

以下是使用 sp_settriggerorder 的示例：

sp_settriggerorder @triggername = 'MyTrigger', @order = 'first', @stmttype = 'UPDATE'

重要 第一个和最后一个触发器必须是两个不同的触发器。

可能同时在表上定义了 INSERT、UPDATE 和 DELETE 触发器。每种语句类型可能都有自己的第一个和最后一个触发器，但它们不能是相同的触发器。

如果为某个表定义的第一个或最后一个触发器不包括触发操作，如 FOR UPDATE、FOR DELETE 或 FOR INSERT，则缺少的操作将没有第一个或最后一个触发器。

不能将 INSTEAD OF 触发器指定为第一个或最后一个触发器。在对基础表进行更新前激发 INSTEAD OF 触发器。然而，如果由 INSTEAD OF 触发器对基础表进行更新，则这些更新将发生于在表上定义触发器（包括第一个触发器）之后。例如，如果视图上的 INSTEAD OF 触发器更新基表并且该基表包含三个触发器，则该三个触发器在 INSTEAD OF 触发器插入数据之前激发。有关更多信息，请参见指定触发器何时激发。

如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器，则将除去 First 或 Last 特性，并且顺序值将设置为 None；必须用 sp_settriggerorder 重置此顺序。

OBJECTPROPERTY 函数使用属性 ExecIsFirstTrigger 和 ExecIsLastTrigger 报告触发器的顺序是第一个还是最后一个。

复制将为本身是即时更新订户或排队更新订户的任何表自动生成第一个触发器。复制要求它的触发器是第一个触发器。如果尝试使具有第一个触发器的表成为即时更新订户或排队更新订户，复制将引发错误。如果使表成为即时更新订户或排队更新订户之后使用户定义触发器成为第一个触发器，则 sp_settriggerorder 会返回一个错误。如果在复制触发器上使用 ALTER，或使用 sp_settriggerorder 将复制触发器更改为最后触发器或无触发器，则订阅将不能正确工作。

请参见

OBJECTPROPERTY

sp_settriggerorder


使用嵌套触发器

如果一个触发器在执行操作时引发了另一个触发器，而这个触发器又接着引发下一个触发器……这些触发器就是嵌套触发器。触发器可嵌套至 32 层，并且可以控制是否可以通过"嵌套触发器"服务器配置选项进行触发器嵌套。

如果允许使用嵌套触发器，且链中的一个触发器开始一个无限循环，则超出嵌套级，而且触发器将终止。

可使用嵌套触发器执行一些有用的日常工作，如保存前一触发器所影响行的一个备份。例如，可以在 titleauthor 上创建一个触发器，以保存由 delcascadetrig 触发器所删除的 titleauthor 行的备份。在使用 delcascadetrig 时，从 titles 中删除title_id PS2091 将删除 titleauthor 中相应的一行或多行。要保存数据，可在 titleauthor 上创建 DELETE 触发器，该触发器的作用是将被删除的数据保存到另一个单独创建的名为 del_save 表中。例如：

CREATE TRIGGER savedel
   ON titleauthor
FOR DELETE
AS
   INSERT del_save
   SELECT * FROM deleted

不推荐按依赖于顺序的序列使用嵌套触发器。应使用单独的触发器层叠数据修改。

说明 由于触发器在事务中执行，如果在一系列嵌套触发器的任意层中发生错误，则整个事务都将取消，且所有的数据修改都将回滚。在触发器中包含 PRINT 语句，用以确定错误发生的位置。

递归触发器

触发器不会以递归方式自行调用，除非设置了 RECURSIVE_TRIGGERS 数据库选项。有两种不同的递归方式：

  • 直接递归

    即触发器激发并执行一个操作，而该操作又使同一个触发器再次激发。例如，一应用程序更新了表 T3，从而引发触发器 Trig3。Trig3 再次更新表 T3，使触发器 Trig3 再次被引发。

  • 间接递归

    即触发器激发并执行一个操作，而该操作又使另一个表中的某个触发器激发。第二个触发器使原始表得到更新，从而再次引发第一个触发器。例如，一应用程序更新了表 T1，并引发触发器 Trig1。Trig1 更新表 T2，从而使触发器 Trig2 被引发。Trig2 转而更新表 T1，从而使 Trig1 再次被引发。

当将 RECURSIVE_TRIGGERS 数据库选项设置为 OFF 时，仅防止直接递归。若要也禁用间接递归，请将 nested triggers 服务器选项设置为 0。

示例
A. 使用递归触发器解决自引用关系

递归触发器的一种用法是用于带有自引用关系的表（亦称为传递闭包）。例如，表 emp_mgr 定义了：

  • 一个公司的雇员 (emp)。

  • 每个雇员的经理 (mgr)。

  • 组织树中向每个经理汇报的雇员总数 (NoOfReports)。

递归 UPDATE 触发器在插入新雇员记录的情况下可以使 NoOfReports 列保持最新。INSERT 触发器更新经理记录的 NoOfReports 列，而该操作递归更新管理层向上其它记录的 NoOfReports 列。

USE pubs
GO
-- Turn recursive triggers ON in the database.
ALTER DATABASE pubs
   SET RECURSIVE_TRIGGERS ON
GO
CREATE TABLE emp_mgr (
   emp char(30) PRIMARY KEY,
    mgr char(30) NULL FOREIGN KEY REFERENCES emp_mgr(emp),
    NoOfReports int DEFAULT 0
)
GO
CREATE TRIGGER emp_mgrins ON emp_mgr
FOR INSERT
AS
DECLARE @e char(30), @m char(30)
DECLARE c1 CURSOR FOR
   SELECT emp_mgr.emp
   FROM   emp_mgr, inserted
   WHERE emp_mgr.emp = inserted.mgr

OPEN c1
FETCH NEXT FROM c1 INTO @e
WHILE @@fetch_status = 0
BEGIN
   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Add 1 for newly
   WHERE emp_mgr.emp = @e                            -- added employee.

   FETCH NEXT FROM c1 INTO @e
END
CLOSE c1
DEALLOCATE c1
GO
-- This recursive UPDATE trigger works assuming:
--   1. Only singleton updates on emp_mgr.
--   2. No inserts in the middle of the org tree.
CREATE TRIGGER emp_mgrupd ON emp_mgr FOR UPDATE
AS
IF UPDATE (mgr)
BEGIN
   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports + 1 -- Increment mgr's
   FROM inserted                            -- (no. of reports) by
   WHERE emp_mgr.emp = inserted.mgr         -- 1 for the new report.

   UPDATE emp_mgr
   SET emp_mgr.NoOfReports = emp_mgr.NoOfReports - 1 -- Decrement mgr's
   FROM deleted                             -- (no. of reports) by 1
   WHERE emp_mgr.emp = deleted.mgr          -- for the new report.
END
GO
-- Insert some test data rows.
INSERT emp_mgr(emp, mgr) VALUES ('Harry', NULL)
INSERT emp_mgr(emp, mgr) VALUES ('Alice', 'Harry')
INSERT emp_mgr(emp, mgr) VALUES ('Paul', 'Alice')
INSERT emp_mgr(emp, mgr) VALUES ('Joe', 'Alice')
INSERT emp_mgr(emp, mgr) VALUES ('Dave', 'Joe')
GO
SELECT * FROM emp_mgr
GO
-- Change Dave's manager from Joe to Harry
UPDATE emp_mgr SET mgr = 'Harry'
WHERE emp = 'Dave'
GO
SELECT * FROM emp_mgr
GO

以下是更新前的结果：

emp                            mgr                           NoOfReports
------------------------------ ----------------------------- -----------
Alice                          Harry                          2
Dave                           Joe                            0
Harry                          NULL                           1
Joe                            Alice                          1
Paul                           Alice                          0

以下为更新后的结果：

emp                            mgr                           NoOfReports
------------------------------ ----------------------------- -----------
Alice                          Harry                          2
Dave                           Harry                          0
Harry                          NULL                           2
Joe                            Alice                          0
Paul                           Alice                          0

设置nested triggers 选项

Transact-SQL

企业管理器

企业管理器

SQL-DMO

设置递归触发器数据库选项

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

CREATE TRIGGER

nested triggers 选项


使用 INSTEAD OF 触发器

INSTEAD OF 触发器替代触发语句的标准操作（INSERT、UPDATE 或 DELETE)。例如，可以定义 INSTEAD OF 触发器在一列或多列上执行错误或值的检查，然后在插入记录之前执行其它操作 例如，当工资表中小时工资列的更新值超过指定值时，可以定义触发器或者产生错误信息并回滚该事务，或者在审核日志中插入新记录（在工资表中插入该记录之前）。有关更多信息，请参见设计 INSTEAD OF 触发器。

可以在表或视图上定义 INSTEAD OF 触发器；然而，INSTEAD OF 触发器对扩展视图能支持的更新类型最有用。例如，INSTEAD OF 触发器能够提供逻辑以通过视图修改多个基表，或者修改包含以下列的基表：

  • timestamp 数据类型

  • 计算列

  • 标识列


INSTEAD OF INSERT 触发器

可以在视图或表上定义 INSTEAD OF INSERT 触发器来代替 INSERT 语句的标准操作。通常，在视图上定义 INSTEAD OF INSERT 触发器以在一个或多个基表中插入数据。

视图选择列表中的列可为空也可不为空。如果视图列不允许为空，则 INSERT 语句必须为该列提供值。如果定义视图列的表达式包括以下项目，则视图列允许为空：

  • 对任何允许为空的基表列的引用。

  • 算术运算符。

  • 对函数的引用。

  • 具有可为空的子表达式的 CASE 或 COALESCE。

  • NULLIF。

可以使用 COLUMNPROPERTY 函数报告的 AllowsNull 属性来确定视图列是否允许为空。sp_help 存储过程还报告哪些视图列允许为空。

引用具有 INSTEAD OF INSERT 触发器的视图的 INSERT 语句必须为每个不允许为空的视图列提供值。这包括引用不能为其指定输入值的基表列的视图列：

  • 基表中的计算列。

  • IDENTITY INSERT 为 OFF 的基表中的标识列。

  • 具有 timestamp 数据类型的基表列。

如果 INSTEAD OF INSERT 视图触发器使用 inserted 表中的数据对基表生成 INSERT，则它应当通过排除 INSERT 语句选择列表中的列忽略这些类型的列值。INSERT 语句可为这些类型的列生成虚值。

例如，因为 INSERT 语句必须为映射到基表中标识列或计算列的视图列指定值，所以它可提供占位符值。INSTEAD OF 触发器在构成将值插入基表的 INSERT 语句时会忽略提供的值。

下面的语句创建表、视图和触发器，以解释这一过程：

CREATE TABLE BaseTable
  (PrimaryKey     int IDENTITY(1,1)
   Color          nvarchar(10) NOT NULL,
   Material       nvarchar(10) NOT NULL,
   ComputedCol AS (Color + Material)
  )
GO

--Create a view that contains all columns from the base table.
CREATE VIEW InsteadView
AS SELECT PrimaryKey, Color, Material, ComputedCol
FROM BaseTable
GO

--Create an INSTEAD OF INSERT trigger on the view.
CREATE TRIGGER InsteadTrigger on InsteadView
INSTEAD OF INSERT
AS
BEGIN
  --Build an INSERT statement ignoring inserted.PrimaryKey and
  --inserted.ComputedCol.
  INSERT INTO BaseTable
       SELECT Color, Material
       FROM inserted
END
GO

直接引用 BaseTable 的 INSERT 语句不能为 PrimaryKey 和 ComputedCol 列提供值。例如：

--A correct INSERT statement that skips the PrimaryKey and ComputedCol columns.
INSERT INTO BaseTable (Color, Material)
       VALUES (N'Red', N'Cloth')

--View the results of the INSERT statement.
SELECT PrimaryKey, Color, Material, ComputedCol
FROM BaseTable

--An incorrect statement that tries to supply a value for the
--PrimaryKey and ComputedCol columns.
INSERT INTO BaseTable
       VALUES (2, N'Green', N'Wood', N'GreenWood')

然而，引用 InsteadView 的 INSERT 语句必须为 PrimaryKey 和 ComputedCol 列提供值：

--A correct INSERT statement supplying dummy values for the
--PrimaryKey and ComputedCol columns.
INSERT INTO InsteadView (PrimaryKey, Color, Material, ComputedCol)
       VALUES (999, N'Blue', N'Plastic', N'XXXXXX')
--View the results of the INSERT statement.
SELECT PrimaryKey, Color, Material, ComputedCol
FROM InsteadView

传递到 InsteadTrigger 的 inserted 表由不可为空的 PrimaryKey 和 ComputedCol 列构成，所以引用该视图的 INSERT 语句必须提供那些列的值。值 999 和 N'XXXXXX' 传递到 InsteadTrigger，但是触发器中的 INSERT 语句没有选择 inserted、PrimaryKey 或 ComputedCol，因此忽略该值。实际插入 BaseTable 的行在 PrimaryKey 中有 2，在 ComputedCol 有 N'BluePlastic'。

在表上指定的 INSTEAD OF INSERT 触发器和在视图上指定的 INSTEAD OF 触发器，其 inserted 表中包含的计算列、标识列和 timestamp 列的值不同。

基表列 | 表上任何 INSERT 触发器的 inserted 表中的值 | 视图上 INSTEAD OF INSERT 触发器 inserted 表中的值
是计算列。 | 计算表达式 | 用户指定值或 NULL
具有 IDENTITY 属性。 | 如果 IDENTITY_INSERT 为 OFF 则为 0，如果 IDENTITY_INSERT 为 ON 则为指定值 | 用户指定值或 NULL
具有 timestamp 数据类型。 | 若该列不允许空值则为二进制零，若该列允许空值则为 NULL | 用户指定值或 NULL

直接引用基表的 INSERT 语句不必为同样具有 DEFAULT 定义的 NOT NULL 列提供值。如果 INSERT 语句不提供值，则使用默认值。然而，如果含有 DEFAULT 定义的 NOT NULL 列由含有 INSTEAD OF INSERT 触发器的视图中的简单表达式引用，则任何引用该视图的 INSERT 语句必须提供列值。该值对于生成传递给触发器的 inserted 表是必需的。对发信号通知触发器应使用默认值的值必须有相应的规则。最好的规则是让 INSERT 语句提供默认值。

INSTEAD OF INSERT 触发器的 deleted 表总是空的。

请参见

COLUMNPROPERTY

sp_help


INSTEAD OF UPDATE 触发器

可在视图上定义 INSTEAD OF UPDATE 触发器以代替 UPDATE 语句的标准操作。通常，在视图上定义 INSTEAD OF UPDATE 触发器以便修改一个或多个基表中的数据。

引用带有 INSTEAD OF UPDATE 触发器的视图的 UPDATE 语句必须为 SET 子句中引用的所有不可为空的视图列提供值。该操作包括在基表中引用列的视图列（该基表不能指定输入值），如：

  • 基表中的计算列。

  • IDENTITY INSERT 设置为 OFF 的基表中的标识列。

  • 具有 timestamp 数据类型的基表列。

通常，当引用表的 UPDATE 语句试图设置计算列、标识列或 timestamp 列的值时会产生错误信息，因为这些列的值必须由 Microsoft® SQL Server™ 决定。这些列必须包含在 UPDATE 语句中，以便满足该行的 NOT NULL 需要。然而，如果 UPDATE 语句引用带 INSTEAD OF UPDATE 触发器的视图，该视图中定义的逻辑能够回避这些列并避免错误。为此，INSTEAD OF UPDATE 触发器必须不试图更新基表中相应列的值。通过不将这些列包含在 UPDATE 语句的 SET 子句中即可达到此目的。在 inserted 表中处理记录时，计算列、标识列或 timestamp 列可以包含虚值以满足 NOT NULL 列的需要，但是 INSTEAD OF UPDATE 触发器忽略这些列值并且由 SQL Server 设置正确的值。

由于 INSTEAD OF UPDATE 触发器不必在未更新的 inserted 列中处理数据，因此该解决方法起作用。在传递到 INSTEAD OF UPDATE 触发器的 inserted 表中，SET 子句中指定的列遵从与 INSTEAD OF INSERT 触发器中的 inserted 列相同的规则。对于在 SET 子句中未指定的列，inserted 表包含在发出 UPDATE 语句前已存在的值。触发器可以通过使用 IF UPDATED(column) 子句来测试特定的列是否已更新。有关更多信息，请参见 INSTEAD OF INSERT 触发器。

INSTEAD OF UPDATE 触发器仅应在 WHERE 子句搜索条件中使用为计算列、标识列或 timestamp 列提供的值。

视图上的 INSTEAD OF UPDATE 触发器应该用来处理为计算列、标识列、timestamp 列或默认列提供的更新值的逻辑与应用于这些列类型的插入值的逻辑相同。

说明 在含有用 UPDATE 操作定义的外键的表上，不能定义 INSTEAD OF UPDATE 触发器。


INSTEAD OF DELETE 触发器

可以在视图或表中定义 INSTEAD OF DELETE 触发器，以代替 DELETE 语句的标准操作。通常，在视图上定义 INSTEAD OF DELETE 触发器以便在一个或多个基表中修改数据。

DELETE 语句不指定对现有数据类型的修改。DELETE 语句只指定要删除的行。传递给 DELETE 触发器的 inserted 表总是空的。发送给 DELETE 触发器的 deleted 表包含在发出 UPDATE 语句前就存在的行的映像。如果在视图或表上定义 INSTEAD OF DELETE 触发器，则 deleted 表的格式以为视图定义的选定列表的格式为基础。

说明 INSTEAD OF DELETE 触发器不能在含有用 DELETE 操作定义的外键的表上定义。


INSTEAD OF 触发器中的表达式和计算列

视图的选择列表可以包含除简单表达式（仅由一个列名构成）以外的其它表达式。这些视图上的 INSTEAD OF 触发器必须具有相应的逻辑，能够正确地确定 INSERT 和 UPDATE 触发器上指定的值中哪些值必须设置到基表中的列。此类表达式的示例包括：

  • 不映射到任何表中任何列的视图表达式，如常数或某些类型的函数。

  • 映射到多列的视图表达式，如由两个或更多列的串联字符串组成的复杂表达式。

  • 转换单个基表列的值的视图表达式，如引用函数中的列。

这些情况还适合作为简单表达式的视图列，这些视图列引用基表中的计算列。定义计算列的表达式可与视图选择列表中的更复杂表达式具有相同的形式。

视图可在其不映射到任何基表列的选择列表中包含表达式，例如：

CREATE VIEW ExpressionView
AS
SELECT *, GETDATE() AS TodaysDate
FROM Northwind.dbo.Employees

虽然 TodaysDate 列并不映射到任何表列，但是 Microsoft® SQL Server™ 2000 必须在将其传递到在 ExpressionView 上定义的 INSTEAD OF 触发器的 inserted 表中生成 TodaysDate 列。然而 inserted.TodaysDate 列可为空，所以引用 ExpressionView 的 INSERT 不必为该列提供值。因为表达式不映射到表中的列，所以触发器会忽略 INSERT 在该列中提供的任何值。

相同的方法应该应用于简单视图表达式，这些表达式引用基表中的计算列，而基表也生成不依赖于其它列的结果，例如：

CREATE TABLE ComputedExample
   (
    PrimaryKey    int PRIMARY KEY,
    ComputedCol   AS SUSER_NAME()
   )

一些复杂表达式映射到多列：

CREATE TABLE SampleTable
     (
      PriKey    int,
      FirstName nvarchar(20),
      LastName  nvarchar(30)
     )
GO
CREATE VIEW ConcatView
AS
SELECT PriKey, FirstName + ' ' + LastName AS CombinedName
FROM SampleTable

ConcatView 中的表达式 CombinedName 包含由 FirstName 和 LastName 值串联起来的值。如果在 ConcatView 上定义 INSTEAD OF INSERT 触发器，则对 INSERT 语句如何提供 CombinedName 列值必须有相应的规则，让触发器确定字符串的哪部分应放在 FirstName 列中，哪部分应放在 LastName 列中。如果选择的规则是让 INSERT 语句使用 'first_name;last_name' 规则指定 CombinedName 的值，则该触发器可成功地处理 INSERT：

CREATE TRIGGER InsteadSample on ConcatView
INSTEAD OF INSERT
AS
BEGIN

   INSERT INTO SampleTable
      SELECT PriKey,
         -- Pull out the first name string.
         SUBSTRING(
            CombinedName,
            1,
            (CHARINDEX(';', CombinedName) - 1)
            ),
         -- Pull out the last name string.
         SUBSTRING(
            CombinedName,
            (CHARINDEX(';', CombinedName) + 1),
            DATALENGTH(CombinedName)
            )
      FROM inserted
END

需要类似的逻辑来处理本身是简单表达式，但引用的计算列中包含复杂表达式的视图列。

一些视图表达式可转换基表列的值，例如通过执行数学运算或使用列作为函数的参数。在这种情况下，INSTEAD OF INSERT 触发器中的逻辑可采用两种方法：

  • 规则可以是：所有 INSERT 语句提供要放在基表中的原始值，而触发器逻辑将该值从 inserted 表移到基表。

  • 规则可以是：所有 INSERT 语句提供预期得到的由视图上的 SELECT 返回的值，在这种情况下触发器中的逻辑必须反转此操作。例如：
    CREATE TABLE BaseTable
  (
   PrimaryKey   int PRIMARY KEY,
   ColumnB      int,
   ColumnC      decimal(19,3)
  )

CREATE VIEW SquareView AS
SELECT PrimaryKey, ColumnB,
       -- Square the value of ColumnC
       SQUARE(ColumnC) AS SquareC
FROM BaseTable

CREATE TRIGGER SquareTrigger ON SquareView
INSTEAD OF INSERT
AS
BEGIN
  INSERT INTO BaseTable
     SELECT PrimaryKey, ColumnB,
             -- Perform logical inverse of function in view.
             SQRT(SquareC)
     FROM inserted
END

对于某些表达式（如使用加法和减法等数学运算的复杂表达式），用户也许不可能提供特定的值供触发器明确地生成目的基表列的值。例如，如果视图选择列表包含表达式 IntColA + IntColB AS AddedColumns，那么 inserted.AddedColumns 中的值 10 表示什么？10 是3 + 7、2 + 8 还是 5 + 5 的结果？仅从 inserted.AddedColumns 值没有办法得知什么值应放置在 IntColA 和 IntColB 中。

在这些情况下，可对触发器进行编码以使用备用信息源确定基表列中要设置的值。对于具有 INSTEAD OF 触发器的视图，视图选择列表必须包含足够的信息，以生成由触发器修改的基表中所有非空列的值。并不是所有的数据都必须直接来自 inserted 表。在一些情况下，inserted 表中的值可以是触发器用于检索其它基表中相关数据的键值。


在 INSTEAD OF 触发器中使用 text、ntext 和 image 数据

数据修改可能包含 text、ntext 和 image 列。在基表中，存储于 text、ntext 或 image 列的值是指向容纳数据的页的文本指针。有关更多信息，请参见 text、ntext 和 image 数据。

虽然 AFTER 触发器不支持 inserted 和 deleted 表中的 text、ntext 或 image 数据，而 INSTEAD OF 触发器却支持。text、ntext 和 image 数据存储在 inserted 表和 deleted 表中，与数据存储在基表中的方法不同。text、ntext 和 image 数据不以单独的页链形式存储。相反，它们在每行内以连续字符串形式存储，这意味着在 inserted 和 deleted 表中 text、ntext 或 image 列内没有文本指针。TEXTPTR 和 TEXTVALID 函数以及 READTEXT、UPDATETEXT 和 WRITETEXT 语句对 inserted 或 deleted 表中的 text、ntext 或 image 列无效。支持 text、ntext 或 image 列的所有其它用途，如在选择列表、WHERE 子句搜索条件或者 SUBSTRING、PATINDEX 或 CHARINDEX 函数中引用它们。INSTEAD OF 触发器中的对 text、ntext 或 image 数据的操作受当前 SET TEXTSIZE 选项的影响，可使用 @@TEXTSIZE 函数确定该选项。

存储在 inserted 和 deleted 表中的 text、ntext 或 image 数据类型因触发操作（INSERT、UPDATE 或 DELETE）而异：

  • 对于 INSERT 语句，inserted 表包含 text、ntext 或 image 列的新值。而 deleted 表没有行。

  • 对于 DELETE 语句，inserted 表没有行，并且 deleted 表行包含 DELETE 启动前 text、ntext 或 image 列所具有的值。

  • 对于不更改 text、ntext 或 image 值的 UPDATE 语句，inserted 和 deleted 表行都包含与 text、ntext 或 image 列相同的值。

  • 对于更改 text、ntext 或 image 值的 UPDATE 语句，deleted 表包含 UPDATE 启动前存在的数据值，并且 inserted 表包含 SET 子句中指定的已进行任何修改的数据。

如果 INSERT、UPDATE 或 DELETE 语句使用较大的 text、ntext 或 image 值修改许多行，则需要相当大的内存来容纳 inserted 和 deleted 表中的 text、ntext 或 image 数据复本。复制这些大量的数据还会降低性能。只要有可能，引用具有 INSTEAD OF 触发器的视图或表的 INSERT、UPDATE 和 DELETE 语句应一次修改一行或一次只修改几行。


用隐性和显式空值激活触发器

在列中插入显式空值，或使用 DEFAULT 关键字为列赋值，都可以按预期激活触发器。同样，当没有在 INSERT 语句中为列指定值时，触发器仍可以在下列条件下激活：

  • 由于不存在 DEFAULT 定义，列中插入了一个隐性空值。

  • 由于 DEFAULT 定义确实存在，列中插入了一个默认值。
示例
A. 用空值和默认值测试触发器激活

下列示例表明触发器如何受隐性和显式空值的影响。创建小型表以容纳两个触发器的值。一列包含空值，另一列包含默认值。触发器评价上述两列是否需要修改并且显示激活触发器时的信息。一系列 INSERT 语句通过插入隐性和显式空值的组合来测试触发器激活。

CREATE TABLE t1
(a int NULL, b int NOT NULL DEFAULT 99)
GO

CREATE TRIGGER t1trig
ON t1
FOR INSERT, UPDATE
AS
IF UPDATE(a) AND UPDATE(b)
   PRINT 'FIRING'
GO

--When two values are inserted, the UPDATE is TRUE for both columns and the trigger is activated.
INSERT t1 (a, b)
VALUES (1, 2)

--When two values are updated, the UPDATE is TRUE for both columns and the trigger is activated.
UPDATE t1
SET a = 1, b = 2

--When an explicit NULL is inserted in column a, the UPDATE is TRUE for both columns and the trigger is activated.
INSERT t1
VALUES (NULL, 2)

--When an explicit NULL is updated in column a, the UPDATE is TRUE for both columns,the trigger is activated.
UPDATE t1
SET a = NULL, b = 2

--When an implicit NULL is inserted in column a, the UPDATE is TRUE for both columns and the trigger is activated.
INSERT t1 (b)
VALUES (2)

--When column a is updated with an implicit NULL, the UPDATE is FALSE for both columns and the trigger is not activated.
UPDATE t1
SET b = 2

--When the default value is implicitly inserted in column b, the UPDATE is TRUE for both columns and the trigger is activated.
INSERT t1 (a)
VALUES (2)

--When column b is updated with an implicit NULL, the UPDATE is FALSE for both columns and the trigger is not activated.
UPDATE t1
SET a = 2

--When the default value is explicitly inserted in column b, the UPDATE is TRUE for both columns and the trigger is activated.
INSERT t1 (a, b)
VALUES (2, DEFAULT)

--When column b is updated explicitly with the default value, the UPDATE is TRUE for both columns and the trigger is activated.
UPDATE t1
SET a = 2, b = DEFAULT

请参见

DEFAULT 定义

空值


修改和重命名触发器

如果需要修改触发器的定义，只需一个操作即可除去并重新创建触发器，或重新定义已有触发器。

如果更改触发器引用的对象名，则必须修改触发器使其文本反映新的名称。因此，在重命名对象前，首先显示该对象的相关性，以确定所建议的更改是否会影响任何触发器。

也可以重命名触发器。新名称必须遵守标识符规则。您只能重命名自己拥有的触发器，而数据库所有者可以更改任意用户的触发器名称。需重命名的触发器必须位于当前数据库中。

也可将触发器修改为对定义进行加密。

修改触发器

Transact-SQL

企业管理器

企业管理器

SQL-DMO

重命名触发器

Transact-SQL

SQL-DMO

说明 重命名触发器并不会更改它在触发器定义文本中的名称。要在定义中更改触发器的名称，应直接修改触发器。

查看触发器的相关性

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

获取关于视图的信息

查看表


查看触发器

在 Microsoft® SQL Server™ 2000 中可确定表中触发器的类型、触发器名称、触发器所有者，以及触发器创建的日期。

您还可以：

  • 获取触发器定义的有关信息，前提是触发器没有在创建或修改时进行加密。可能需要查看触发器定义以了解它使用的 Transact-SQL 语句，或了解它如何影响所在的表。

  • 列出指定的触发器所使用的对象。该信息可在数据库中的对象发生更改或删除时用来标识这些对象。

sp_helptrigger 的结果集包含 isafter 和 isinsteadof 列，以报告触发器是 AFTER 触发器还是 INSTEAD OF 触发器。OBJECTPROPERTY 函数通过 ExecIsInsteadOfTrigger 和 ExecIsAfterTrigger 属性报告触发器是 AFTER 还是 INSTEAD OF 触发器。

查看表中的触发器类型

Transact-SQL

SQL-DMO

查看触发器的有关信息

Transact-SQL

SQL-DMO

查看触发器

Transact-SQL

企业管理器

企业管理器

SQL-DMO

查看触发器的相关性

Transact-SQL

企业管理器

企业管理器

SQL-DMO

请参见

OBJECTPROPERTY

sp_helptrigger


删除触发器

当不再需要某个触发器时，可将其删除。当触发器被删除时，它所基于的表和数据并不受影响。删除表将自动删除其上的所有触发器。删除触发器的权限默认授予在该触发器所在表的所有者。

删除触发器

Transact-SQL

企业管理器

企业管理器

SQL-DMO


用户定义函数

函数是由一个或多个 Transact-SQL 语句组成的子程序，可用于封装代码以便重新使用。Microsoft® SQL Server™ 2000 并不将用户限制在定义为 Transact-SQL 语言一部分的内置函数上，而是允许用户创建自己的用户定义函数。

可使用 CREATE FUNCTION 语句创建、使用 ALTER FUNCTION 语句修改、以及使用 DROP FUNCTION 语句除去用户定义函数。每个完全合法的用户定义函数名 (database_name.owner_name.function_name) 必须唯一。

必须被授予 CREATE FUNCTION 权限才能创建、修改或除去用户定义函数。不是所有者的用户在 Transact-SQL 语句中使用某个函数之前，必须先给此用户授予该函数的适当权限。若要创建或更改在 CHECK 约束、DEFAULT 子句或计算列定义中引用用户定义函数的表，还必须具有函数的 REFERENCES 权限。

在函数中，区别处理导致删除语句并且继续在诸如触发器或存储过程等模式中的下一语句的 Transact-SQL 错误。在函数中，上述错误会导致停止执行函数。接下来该操作导致停止唤醒调用该函数的语句。

用户定义函数的类型

SQL Server 2000 支持三种用户定义函数：

  • 标量函数

  • 内嵌表值函数

  • 多语句表值函数

用户定义函数采用零个或更多的输入参数并返回标量值或表。函数最多可以有 1024 个输入参数。当函数的参数有默认值时，调用该函数时必须指定默认 DEFAULT 关键字才能获取默认值。该行为不同于在存储过程中含有默认值的参数，而在这些存储过程中省略该函数也意味着省略默认值。用户定义函数不支持输出参数。

标量函数返回在 RETURNS 子句中定义的类型的单个数据值。可以使用所有标量数据类型，包括 bigint 和 sql_variant。不支持 timestamp 数据类型、用户定义数据类型和非标量类型（如 table 或 cursor）。在 BEGIN...END 块中定义的函数主体包含返回该值的 Transact-SQL 语句系列。返回类型可以是除 text、ntext、image、cursor 和 timestamp 之外的任何数据类型。

表值函数返回 table。对于内嵌表值函数，没有函数主体；表是单个 SELECT 语句的结果集。对于多语句表值函数，在 BEGIN...END 块中定义的函数主体包含 TRANSACT-SQL 语句，这些语句可生成行并将行插入将返回的表中。有关内嵌表值函数的更多信息，请参见内嵌用户定义函数。有关表值函数的更多信息，请参见返回 table 数据类型的用户定义函数。

BEGIN...END 块中的语句不能有任何副作用。函数副作用是指对具有函数外作用域（例如数据库表的修改）的资源状态的任何永久性更改。函数中的语句唯一能做的更改是对函数上的局部对象（如局部游标或局部变量）的更改。不能在函数中执行的操作包括：对数据库表的修改，对不在函数上的局部游标进行操作，发送电子邮件，尝试修改目录，以及生成返回至用户的结果集。

函数中的有效语句类型包括：

  • DECLARE 语句，该语句可用于定义函数局部的数据变量和游标。

  • 为函数局部对象赋值，如使用 SET 给标量和表局部变量赋值。

  • 游标操作，该操作引用在函数中声明、打开、关闭和释放的局部游标。不允许使用 FETCH 语句将数据返回到客户端。仅允许使用 FETCH 语句通过 INTO 子句给局部变量赋值。

  • 控制流语句。

  • SELECT 语句，该语句包含带有表达式的选择列表，其中的表达式将值赋予函数的局部变量。

  • INSERT、UPDATE 和 DELETE 语句，这些语句修改函数的局部 table 变量。

  • EXECUTE 语句，该语句调用扩展存储过程。

在查询中指定的函数的实际执行次数在优化器生成的执行计划间可能不同。示例为 WHERE 子句中的子查询唤醒调用的函数。子查询及其函数执行的次数会因优化器选择的访问路径而异。

用户定义函数中不允许使用会对每个调用返回不同数据的内置函数。用户定义函数中不允许使用以下内置函数：

@@CONNECTIONS | @@PACK_SENT | GETDATE
@@CPU_BUSY | @@PACKET_ERRORS | GetUTCDate
@@IDLE | @@TIMETICKS | NEWID
@@IO_BUSY | @@TOTAL_ERRORS | RAND
@@MAX_CONNECTIONS | @@TOTAL_READ | TEXTPTR
@@PACK_RECEIVED | @@TOTAL_WRITE |

架构绑定函数

CREATE FUNCTION 支持 SCHEMABINDING 子句，后者可将函数绑定到它引用的任何对象（如表、视图和其它用户定义函数）的架构。尝试对架构绑定函数所引用的任何对象执行 ALTER 或 DROP 都将失败。

必须满足以下条件才能在 CREATE FUNCTION 中指定 SCHEMABINDING：

  • 该函数所引用的所有视图和用户定义函数必须是绑定到架构的。

  • 该函数所引用的所有对象必须与函数位于同一数据库中。必须使用由一部分或两部分构成的名称来引用对象。

  • 必须具有对该函数中引用的所有对象（表、视图和用户定义函数）的 REFERENCES 权限。

可使用 ALTER FUNCTION 删除架构绑定。ALTER FUNCTION 语句将通过不带 WITH SCHEMABINDING 指定函数来重新定义函数。

调用用户定义函数

当调用标量用户定义函数时，必须提供至少由两部分组成的名称：

SELECT *, MyUser.MyScalarFunction()
FROM MyTable

可以使用一个部分构成的名称调用表值函数：

SELECT *
FROM MyTableFunction()

然而，当调用返回表的 SQL Server 内置函数时，必须将前缀 :: 添加至函数名：

SELECT * FROM ::fn_helpcollations()

可在 Transact-SQL 语句中所允许的函数返回的相同数据类型表达式所在的任何位置引用标量函数，包括计算列和 CHECK 约束定义。例如，下面的语句创建一个返回 decimal 的简单函数：

CREATE FUNCTION CubicVolume
-- Input dimensions in centimeters
   (@CubeLength decimal(4,1), @CubeWidth decimal(4,1),
    @CubeHeight decimal(4,1) )
RETURNS decimal(12,3) -- Cubic Centimeters.
AS
BEGIN
   RETURN ( @CubeLength * @CubeWidth * @CubeHeight )
END

然后可以在允许整型表达式的任何地方（如表的计算列中）使用该函数：

CREATE TABLE Bricks
   (
    BrickPartNmbr   int PRIMARY KEY,
    BrickColor      nchar(20),
    BrickHeight     decimal(4,1),
    BrickLength     decimal(4,1),
    BrickWidth      decimal(4,1),
    BrickVolume AS
              (
               dbo.CubicVolume(BrickHeight,
                         BrickLength, BrickWidth)
              )
   )

dbo.CubicVolume 是返回标量值的用户定义函数的一个示例。RETURNS 子句定义由该函数返回的值的标量数据类型。BEGIN...END 块包含一个或多个执行该函数的 Transact-SQL 语句。该函数中的每个 RETURN 语句都必须具有一个参数，可返回具有在 RETURNS 子句中指定的数据类型（或可隐性转换为 RETURNS 中指定类型的数据类型）的数据值。RETURN 参数的值是该函数返回的值。

获得有关函数的信息

下面几个目录对象报告有关用户定义函数的信息：

  • sp_help 报告有关用户定义函数的信息。

  • sp_helptext 报告用户定义函数的来源。

三个信息架构视图报告有关用户定义函数的信息：ROUTINES、PARAMETERS 和 ROUTINE_COLUMNS。这些信息架构视图还报告存储过程的信息。


返回 table 数据类型的用户定义函数

返回 table 的用户定义函数可以是替代视图的强大方式。返回 table 的用户定义函数可用在 Transact-SQL 查询中允许表或视图表达式的地方。视图限制于单个 SELECT 语句，而用户定义函数可包含允许比视图中具有的逻辑功能更强大的附加语句。

返回 table 的用户定义函数还可替换返回单个结果集的存储过程。可在 Transact-SQL 语句的 FROM 子句中引用由用户定义函数返回的 table，而返回结果集的存储过程却不能。例如，假设 fn_EmployeesInDept 是返回 table 的用户定义函数并可由 SELECT 语句唤醒调用，如下所示：

SELECT * FROM tb_Employees AS E INNER JOIN
     dbo.fn_EmployeesInDept('shipping') AS EID
   ON E.EmployeeID = EID.EmployeeID

在返回 table 的用户定义函数中：

  • RETURNS 子句为函数返回的表定义局部返回变量名。RETURNS 子句还定义表的格式。局部返回变量名的作用域位于函数内。

  • 函数主体中的 Transact-SQL 语句生成行并将其插入 RETURNS 子句所定义的返回变量。

  • 当执行 RETURN 语句时，插入变量的行以函数的表格格式输出形式返回。RETURN 语句不能有参数。

函数中返回 table 的 Transact-SQL 语句不能直接将结果集返回用户。函数返回用户的唯一信息是由该函数返回的 table。

下例在 Northwind 数据库中创建一个返回 table 的函数：

CREATE FUNCTION LargeOrderShippers ( @FreightParm money )
RETURNS @OrderShipperTab TABLE
   (
    ShipperID     int,
    ShipperName   nvarchar(80),
    OrderID       int,
    ShippedDate   datetime,
    Freight       money
   )
AS
BEGIN
   INSERT @OrderShipperTab
        SELECT S.ShipperID, S.CompanyName,
               O.OrderID, O.ShippedDate, O.Freight
        FROM Shippers AS S INNER JOIN Orders AS O
              ON S.ShipperID = O.ShipVia
        WHERE O.Freight > @FreightParm
   RETURN
END

在这个函数中，返回的本地变量名是 @OrderShipperTab。函数主体中的语句将行插入变量 @OrderShipperTab，以生成该函数返回的表结果。

下面的查询在其 FROM 子句中引用函数返回的表：

SELECT *
FROM LargeOrderShippers( $500 )

说明 对于用户定义函数返回的表，text in row 表选项自动设置为 256。该值无法更改。不能使用 READTEXT、WRITETEXT 和 UPDATETEXT 语句来读取或写入该表中的任何 text、ntext 或 image 列的一部分。有关更多信息，请参见 Text in Row 数据。


内嵌用户定义函数

内嵌用户定义函数是返回 table 的用户定义函数的子集。内嵌函数可用于实现参数化视图的功能。

请看下面的视图：

CREATE VIEW vw_CustomerNamesInWA AS
SELECT CustomerID, CompanyName
FROM Northwind.dbo.Customers
WHERE Region = 'WA'

可创建更通用的版本 vw_CustomerNamesInRegion，方法是将 WHERE Region = 'WA' 替换为 WHERE Region = @RegionParameter 并让用户指定感兴趣的查看区域。然而，视图不支持在 WHERE 子句中指定的搜索条件的参数。

内嵌用户定义函数可用于支持在 WHERE 子句中指定的搜索条件的参数。下面是使用户得以在其选择中指定区域的函数示例：

CREATE FUNCTION fn_CustomerNamesInRegion
                 ( @RegionParameter nvarchar(30) )
RETURNS table
AS
RETURN (
        SELECT CustomerID, CompanyName
        FROM Northwind.dbo.Customers
        WHERE Region = @RegionParameter
       )
GO
-- Example of calling the function for a specific region
SELECT *
FROM fn_CustomerNamesInRegion(N'WA')
GO

内嵌用户定义函数遵从以下规则：

  • RETURNS 子句仅包含关键字 table。不必定义返回变量的格式，因为它由 RETURN 子句中的 SELECT 语句的结果集的格式设置。

  • function_body 不由 BEGIN 和 END 分隔。

  • RETURN 子句在括号中包含单个 SELECT 语句。SELECT 语句的结果集构成函数所返回的表。内嵌函数中使用的 SELECT 语句受到与视图中使用的 SELECT 语句相同的限制。

内嵌函数还可用于提高索引视图的能力。索引视图自身不能在其 WHERE 子句搜索条件中使用参数，针对特定用户的需要调整存储的结果集。然而，可定义存储与视图匹配的完整数据集的索引视图，然后在包含允许用户调整其结果的参数化搜索条件的索引视图上定义内嵌函数。如果视图定义较复杂，则生成结果集所要执行的大多数工作都涉及在视图上创建聚集索引时生成聚合或联接多个表。之后如果创建引用视图的内嵌函数，则该函数可应用用户的参数化筛选，从结果集中提取由 CREATE INDEX 语句生成的特定行。在执行 CREATE INDEX 时复杂的聚合和联接进行一次，随后引用内嵌函数的所有查询都从简化的存储结果集中筛选行。例如：

  1. 定义将所有销售数据聚合到结果集的视图 vw_QuarterlySales，该结果集按季度报告所有商店的汇总销售数据。

  2. 在 vw_QuarterlySales 上创建聚集索引以具体化包含汇总数据的结果集。

  3. 创建筛选汇总数据的内嵌函数：
    CREATE FUNCTION fn_QuarterlySalesByStore
     (
      @StoreID int
     )
RETURNS table
AS
RETURN (
        SELECT *
        FROM SalesDB.dbo.vw_QuarterlySales
        WHERE StoreID = @StoreID
       )
  4. 然后用户可从内嵌函数进行选择以获得其特定商店的数据：
    SELECT *
FROM fn_QuarterlySalesByStore( 14432 )

满足在第 4 步发出的查询所需的大多数工作将按季度聚合销售数据。该工作在第 2 步进行一次。第 4 步中的每个 SELECT 语句都使用函数 fn_QuarterlySalesByStore 筛选出用户的某个商店特有的聚合数据。


确定性函数和非确定性函数

所有的函数都是确定性或非确定性的：

  • 在使用特定的输入值集调用确定性函数的任何时候，它们总是返回相同的结果。

  • 在每次使用特定的输入值集调用非确定性函数时，它们可能返回不同的结果。

函数是否为确定性函数或非确定性函数称为函数的确定性。

例如，DATEADD 内置函数是确定性函数，因为对于其三个参数的任何给定参数值集总是返回相同的结果。GETDATE 不是确定性函数，因为总是使用相同的参数唤醒调用它，而它在每次执行时返回结果都不同。

Microsoft® SQL Server™ 的早期版本没有依赖于函数确定性的功能。在 MicrosoftSQL Server 2000 中，非确定性函数不能指定为两种 Transact-SQL 表达式类型：

  • 如果 computed_column_expression 引用任何非确定性函数，则不能在计算列上创建索引。

  • 如果视图引用任何非确定性函数，则不能在视图上创建聚集索引。

SQL Server 记录的用户定义函数的属性之一是该函数是否为确定性函数。若想在视图或计算列上创建索引，则该视图或计算列都不能唤醒调用非确定性用户定义函数。

用户定义函数的确定性

用户定义函数是确定性函数还是非确定性函数取决于函数是如何编码的。在以下情况中用户定义函数是确定性函数：

  • 函数是架构绑定函数。

  • 所有由用户定义函数调用的函数（包括内置函数或其它用户定义函数）都是确定性函数。

  • 函数主体不引用该函数作用域以外的数据库对象。例如，确定性函数不能引用函数的局部表变量以外的表。

  • 该函数不调用任何扩展存储过程。

不满足以上条件的用户定义函数标记为非确定性函数。另外，内置非确定性函数不能用在用户定义函数的主体中。

内置函数的确定性

不能影响任何内置函数的确定性。每个内置函数都根据 Microsoft SQL Server 如何执行该函数而分为确定性函数或非确定性函数。

所有的聚合函数和字符串内置函数（字符串函数 CHARINDEX 和 PATINDEX 除外）都是确定性函数。有关这些函数的列表，请参见聚合函数和字符串函数。

在除聚合函数和字符串函数以外的内置函数分类中，下列内置函数总是确定性函数：

ABS | DATEDIFF | PARSENAME
ACOS | DAY | POWER
ASIN | DEGREES | RADIANS
ATAN | EXP | ROUND
ATN2 | FLOOR | SIGN
CEILING | ISNULL | SIN
COALESCE | ISNUMERIC | SQUARE
COS | LOG | SQRT
COT | LOG10 | TAN
DATALENGTH | MONTH | YEAR
DATEADD | NULLIF |

下列函数并不总是确定性函数，但是当以确定性方式指定它们时，它们可用于索引视图或计算列上的索引。

函数 | 注释
CAST | 除非与 datetime、smalldatetime 或 sql_variant 一起使用，其它时候都具有确定性。
CONVERT | 除非与 datetime、smalldatetime 或 sql_variant 一起使用，其它时候都具有确定性。如果还指定样式参数，则 datetime 和 smalldatetime 数据类型是确定性的。
CHECKSUM | 确定性函数，CHECKSUM(*) 除外。
ISDATE | 只有当与 CONVERT 函数一起使用、指定 CONVERT 样式参数并且样式不等于 0、100、9 或 109 时才是确定性函数。
RAND | 仅当指定 seed 参数时 RAND 才是确定性函数。

所有的配置、游标、元数据、安全和系统统计函数都是非确定性函数。有关这些函数的列表，请参见配置函数、游标函数、元数据函数、安全函数和系统统计函数。

其它分类中的下列内置函数总是非确定性函数：

@@ERROR | FORMATMESSAGE | NEWID
IDENTITY | GETANSINULL | PATINDEX
@@ROWCOUNT | GETDATE | PERMISSIONS
@@TRANCOUNT | GetUTCDate | SESSION_USER
APP_NAME | HOST_ID | STATS_DATE
CHARINDEX | HOST_NAME | SYSTEM_USER
CURRENT_TIMESTAMP | IDENT_INCR | TEXTPTR
CURRENT_USER | IDENT_SEED | TEXTVALID
DATENAME | IDENTITY | USER_NAME

从函数调用扩展存储过程

由于扩展存储过程对数据库会产生副作用，因此调用扩展存储过程的函数是不确定性的。副作用是对数据库的全局状态的更改，如对表的更新、对文件或网络等外部资源的更新（例如，修改文件或发送电子邮件）。从用户定义函数中执行扩展存储过程时，不应该依赖返回一致性结果集。不推荐对数据库产生副作用的用户定义函数。

从函数内部调用扩展存储过程时，该扩展存储过程不能向客户端返回结果集。任何向客户端返回结果集的开放数据服务 API 将产生 FAIL 的返回代码。

扩展存储过程可以连接返回到 SQL Server；然而，该过程不能联接与唤醒调用扩展存储过程的初始函数相同的事务。

类似于从批处理或存储过程中调用，在 Microsoft Windows® 安全帐户（SQL Server 正在运行）的上下文中执行扩展存储过程。扩展存储过程的所有者在授予其他用户执行该过程的权限时应该考虑该问题。


将存储过程重新编写为函数

本主题描述如何确定是否将现有存储过程逻辑重新编写为用户定义函数。例如，如果希望直接从查询唤醒调用存储过程，可将代码重新打包为用户定义函数。

一般来说，如果存储过程返回一个（单个）结果集，则定义表值函数。如果存储过程计算标量值，则定义标量函数。

表值函数的条件

如果存储过程满足以下条件，则可作为重新编写为表值函数的很好的候选存储过程：

  • 逻辑可在单个 SELECT 语句中表现，但它是存储过程而不是视图，只是由于需要参数。可使用内嵌表值函数处理这种情况。

  • 存储过程不执行更新操作（除了对表变量外）。

  • 不需要动态 EXECUTE 语句。

  • 存储过程返回一个结果集。

  • 存储过程的主要目的是生成要装载到临时表的中间结果，SELECT 语句随后将查询临时表。可使用表值函数编写 INSERT...EXEC 语句。例如，请看下面的序列：
    INSERT #temp EXEC sp_getresults
SELECT ...
    FROM #temp, t1
    WHERE ...

    可将存储过程 sp_getresults 重新编写为表值函数，如 fn_results()，这意味着可将上面的语句重新编写为：

    SELECT ...
    FROM fn_results(), t1
    WHERE ...


在数据库对象上使用扩展属性

Microsoft® SQL Server™ 2000 引入了扩展属性，用户可在各种数据库对象上定义这些属性。这些扩展属性可用于存储与数据库对象有关的应用程序或站点特有的信息。因为属性存储在数据库中，所有读取属性的应用程序都能以相同的方式评估对象。这有助于在系统中的所有程序对数据的处理方式上强制一致性。

每个扩展属性都有用户定义的名称和值。扩展属性值为最多可包含 7,500 字节数据的 sql_variant 值。个别数据库对象可以有多个扩展属性。

扩展属性的可能用途包括：

  • 指定表、视图或列的标题。这样，所有应用程序都可在显示表、视图或列信息的用户界面中使用该标题。

  • 指定列的输入掩码，以便所有应用程序都可在执行 Transact-SQL 语句前验证数据。

  • 指定关于在列中显示数据的格式规则。

  • 记录应用程序可显示给用户的对特定数据库对象的描述。

  • 指定应显示的列大小和窗口位置。

出于指定扩展属性的目的，将 SQL Server 2000 数据库中的对象分为三个等级（0、1、2）。0 级是最高等级，2 级是最低等级。下表列出等级 0 对象，用户和用户定义数据类型，及其有效的等级 1 和等级 2 对象。

等级 0 | 等级 1 | 等级 2
用户 | 表 | 列、索引、约束、触发器
 | 视图 | 列、INSTEAD OF 触发器
 | 架构绑定视图 | 列、索引、INSTEAD OF 触发器
 | 存储过程 | 参数
 | 规则 | <无>
 | 默认值 | <无>
 | 函数 | 列、参数、约束，
 | 架构绑定函数 | 列、参数、约束
用户定义数据类型 | <无> | <无>

没有列为等级 0、1 或 2 的对象不支持扩展属性。

引用某个等级中的对象必须用拥有或包含它们的更高等级对象的名称进行限制。例如，当引用某列（等级 2）时还必须指定包含该列的表（等级 1）和拥有该表的用户（等级 0）。

扩展属性仅提供在其中存储数据的指定位置。必须对所有应用程序进行编码以查询属性并采取适当的操作。例如，将标题属性添加到列并不能创建可由应用程序显示的标题。必须对每个应用程序进行编码才可正确地读取和显示标题。

扩展属性由三个系统存储过程进行管理：

  • sp_addextendedproperty

    将新扩展属性添加到数据库对象中。

  • sp_updateextendedproperty

    更新现有扩展属性的值。

  • sp_dropextendedproperty

    除去现有的扩展属性。

可使用系统函数 FN_LISTEXTENDEDPROPERTY 检索现有扩展属性的值。

下面是具有以下属性的表的示例：

  • 表和列的标题扩展属性。

  • 列的输入掩码扩展属性。
USE Northwind
GO
CREATE TABLE TestExProp
  (PriKey      int PRIMARY KEY IDENTITY(1,1),
   USPhoneNmbr      char(13)
        CHECK (USPhoneNmbr LIKE
               '([0-9][0-9][0-9])[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]'
               ),
   USSocialScrty   char(11)
        CHECK (USSocialScrty LIKE
               '[0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9][0-9][0-9]'
               )
  )
GO
sp_addextendedproperty 'Caption', 'Caption Test Table',
                       'user', dbo, 'table', TestExProp
GO
sp_addextendedproperty 'Caption', 'Primary Key',
                       'user', dbo, 'table', TestExProp, 'column', PriKey
GO
sp_addextendedproperty 'Input Mask', '(NNN)NNN-NNNN',
                       'user', dbo, 'table', TestExProp, 'column', USPhoneNmbr
GO
sp_addextendedproperty 'Caption', 'US Phone Number',
                       'user', dbo, 'table', TestExProp, 'column', USPhoneNmbr
GO
sp_addextendedproperty 'Input Mask', 'NNN-NN-NNNN',
                       'user', dbo, 'table', TestExProp, 'column', USSocialScrty
GO
sp_addextendedproperty 'Caption', 'US Social Security Number',
                       'user', dbo, 'table', TestExProp, 'column', USSocialScrty
GO

下面的语句更新主键标题属性：

sp_updateextendedproperty 'Caption', 'Primary Key - Integer',
                       'user', dbo, 'table', TestExProp, 'column', PriKey

下面的语句除去输入掩码属性：

sp_dropextendedproperty 'Input Mask',
                       'user', dbo, 'table', TestExProp,
                       'column', USSocialScrty
GO
sp_dropextendedproperty 'Input Mask',
                       'user', dbo, 'table', TestExProp,
                       'column', USPhoneNmbr
GO

下面的语句检索表的标题属性：

SELECT *
FROM ::FN_LISTEXTENDEDPROPERTY('Caption', 'User','dbo','table',
                               'TestExProp', default, default)

该示例显示使用 CHECK 约束和输入掩码属性来指定每个列的数据模式。大多数站点选择其中一种方法，除非：

  • CHECK 约束已用作临时度量，直到所有处理该表的程序可改为使用输入掩码属性。

  • 站点还支持可通过特殊工具不读取扩展属性而更新数据的用户。

CHECK 约束上的输入掩码的优点是在应用程序中运用逻辑，这样，如果用户提供格式不正确的数据则可生成更多的信息错误。输入掩码的缺点是需要单独调用 fn_listextendedproperty 以获得属性，并且必须将强制掩码的逻辑添加到所有程序中。

请参见

fn_listextendedproperty

属性管理

sp_addextendedproperty

sp_dropextendedproperty

sp_updateextendedproperty


全文索引

对 Microsoft® SQL Server™ 2000 数据的全文支持涉及两个功能：对字符数据发出查询的能力和创建及维护基础索引以简化这些查询的能力。

全文索引在许多地方与普通的 SQL 索引不同。

普通 SQL 索引 | 全文索引
存储时受定义它们所在的数据库的控制。 | 存储在文件系统中，但通过数据库管理。
每个表允许有若干个普通索引。 | 每个表只允许有一个全文索引。
当对作为其基础的数据进行插入、更新或删除时，它们自动更新。 | 将数据添加到全文索引称为填充，全文索引可通过调度或特定请求来请求，也可以在添加新数据时自动发生。
不分组。 | 在同一个数据库内分组为一个或多个全文目录。
使用 SQL Server 企业管理器、向导或 Transact-SQL 语句创建和除去。 | 使用 SQL Server 企业管理器、向导或存储过程创建、管理和除去。

这些差异使大量管理任务变得不可缺少。全文管理是在几个层次上实施的：

  • 服务器

    可以对服务器范围的某些属性（如 resource_usage）加以设置，以便增加或减少全文服务所使用的系统资源数量。

    说明 全文引擎作为名为 Microsoft 搜索的服务在 Microsoft Windows NT® Server 和 Microsoft Windows® 2000 Server 上运行。对于 Microsoft SQL Server 个人版，Microsoft 搜索服务不可用。尽管这意味着 Microsoft 搜索服务既未安装在 Microsoft Windows 95/98 上，也未安装在 Windows NT 工作站或 Windows 2000 Professional 客户端上，但这些客户端在连接到 SQL Server 标准版安装或企业版实例时可以使用这项服务。

  • 数据库

    必须启用数据库才能使用全文服务。可以在已启用的数据库中创建和除去一个或多个全文目录的元数据。

  • 全文目录

    全文目录包含数据库中的全文索引。每个目录可以用于数据库内的一个或多个表的索引需求。该目录中的索引是使用这里介绍的管理功能来填充的。（全文目录必须驻留在与 SQL Server 实例相关联的本地硬盘驱动器上。不支持可移动的驱动器、软盘和网络驱动器）。在每个服务器上最多可创建 256 个全文目录。

    说明 Windows NT 故障转移群集环境完全支持全文索引。有关更多信息，请参见在故障转移群集中运行全文查询。

  • 表

    首先，必须为全文支持启用表。然后，为与该表相关联的全文索引创建元数据（如表名及其全文目录）。表启用后，可以用为全文支持而启用的列中的数据填充它。如果表的全文定义被更改（例如，添加一个也将为全文检索而索引的新列），则必须重新填充相关的全文目录以使全文索引与新的全文定义同步。

  • 列

    可以从非活动的注册表中添加或除去支持全文查询的列。

在所有这些级别上，可使用工具检索元数据和状态信息。

和常规 SQL 索引一样，当在相关表中修改数据时，可自动更新全文索引。或者，也可以适当的间隔手工重新填充全文索引。这种重写可能既耗时又大量占用资源，因此，在数据库活动较少时，这通常是在后台运行的异步进程。

应将具有相同更新特性的表（如更改少的与更改多的，或在一天的特定时段内频繁更改的表）组合在一起，并分配给相同的全文目录。通过以此方法设置全文目录填充调度，使得全文索引和表保持同步，且在数据库活动较多时不对数据库服务器的资源使用产生负面影响。

为全文目录中的表安排全文索引的位置是非常重要的。在为全文目录指定表时，应该注意下列基本原则：

  • 始终选择可用于全文唯一键的最小唯一索引。（4 个字节且基于整数的索引是最佳的。）这将显著减少文件系统中 Microsoft 搜索服务所需要的资源。如果主键很大（超过 100 字节），可以考虑选择表中其它唯一索引（或创建另一个唯一索引）作为全文唯一键。否则，如果全文唯一键的大小达到允许的上限（450 字节），全文填充将无法继续进行。

  • 如果进行索引的表有成千上万行，请将该表指定给其自己的全文目录。

  • 应该考虑对其进行全文索引的表中发生的更改数以及表的行数。如果要更改的总行数，加上上次全文填充期间表中出现的行数达到成千上万行，请将该表指定给其自己的全文目录。

请参见

sp_fulltext_table


使用 SQL 企业管理器管理全文功能

可以使用全文索引向导或 SQL Server 企业管理器中的快捷菜单管理全文索引。

启用数据库以便进行全文索引

企业管理器

企业管理器

Transact-SQL

启用表以便进行全文索引

企业管理器

企业管理器

Transact-SQL

启用列以便进行全文索引

企业管理器

企业管理器

Transact-SQL

编辑表中的全文索引

企业管理器

企业管理器

Transact-SQL

从表中删除全文索引

企业管理器

企业管理器

Transact-SQL

创建全文目录

企业管理器

企业管理器

Transact-SQL

重建全文目录

企业管理器

企业管理器

Transact-SQL

在数据库中重建所有全文目录

企业管理器

企业管理器

Transact-SQL

启动和停止对全文索引的完全填充或增量填充

企业管理器

企业管理器

Transact-SQL

检查全文目录的状态、表和调度

企业管理器

企业管理器

更改或创建全文目录的新调度

企业管理器

企业管理器

从数据库中删除全文目录

企业管理器

企业管理器

Transact-SQL

从数据库中删除所有全文目录

企业管理器

企业管理器

Transact-SQL

清除服务器上的全文目录

企业管理器

企业管理器

Transact-SQL

为数据库重新填充所有全文目录

企业管理器

企业管理器

Transact-SQL


使用存储过程和标量函数管理全文功能

通过下列主题中提供的示例对如何使用存储过程和标量函数管理全文功能进行了描述：

  • 使其他用户能够发出全文查询

  • 对全文目录执行调查和清除任务

  • 执行不常用的任务

这些主题中的示例使用 pubs 数据库中的两个虚构的表，writers 表和 books 表。下表显示作为全文索引候选对象的 writers 表和列，尤其是 varchar 数据类型。

列名 | 数据类型 | 是否启用全文功能？
writer_name | char(40) | 否
citizenship | char(40) | 否
organization | varchar(100) | 是
royalties_ytd | decimal(7,2) | 否
Royalyties_lifetime | decimal(7,2) | 否
Bio | varchar(500) | 是
writer_id | integer （带有由 writer_id_index 索引强制执行的唯一值） | 否
last_changed | timestamp | 否

下表显示作为全文索引候选对象的 books 表和列，尤其是 varchar 和 text 数据类型。

列名 | 数据类型 | 是否启用全文功能？
writer_name | char(40) | 否
Title | varchar(120) | 是
大小 | smallint | 否
pub_date | datetime | 否
the_words | text | 是
Abstract | varchar(500) | 是
isbn | char(15) （带有由 isbn_index 索引强制执行的唯一值） | 否
last_changed | timestamp | 否

有关查询和索引的更多信息，请参见全文管理与全文查询结合的示例。


使其他用户能够发出全文查询

本示例说明怎样使用 SQL Server 服务管理器使其他用户得以对 writers 表和 books 表发出全文查询。writers 表和 books 表是位于 pubs 数据库中的虚构的表。若要获得对这些表的描述，请参见使用存储过程和标量函数管理全文功能。

  1. 使用 SQL Server 服务管理器验证全文服务 Microsoft 搜索是否在运行。

    若有必要，可以按以下方式之一启动和停止该服务：

    • 使用 SQL Server 企业管理器中的"全文检索"服务的快捷菜单。

    • 使用 SQL Server 服务管理器中的 Microsoft 搜索服务。

    • 在命令提示符处键入 net start mssearch。
  2. 执行下列语句，以查明是否已为全文处理启用了 pubs 数据库：
    SELECT DatabaseProperty ('Pubs',  'IsFulltextEnabled' )

    有关更多信息，请参见 DATABASEPROPERTY。

    如果已启用全文支持，该语句返回 1，否则返回 0。

  3. 如果尚未启用全文支持（这是新建数据库的默认设置），则需为全文处理启用 pubs 数据库。使用 pubs 时，执行下列存储过程：
    sp_fulltext_database  'enable'

    有关更多信息，请参见 sp_fulltext_database。

  4. 通过在 pubs 数据库中执行下列存储过程，创建一个名为 PubsCatalog 的全文目录作为候选的默认目录：
    sp_fulltext_catalog 'PubsCatalog', 'create'

    有关更多信息，请参见 sp_fulltext_catalog。

    这将在数据库系统表中创建有关全文目录的元数据，并在文件系统中生成空的全文目录。

  5. 为全文处理注册 writers 表和 books 表，方法是对每个表分别执行一次下列存储过程：
    sp_fulltext_table  'writers',  'create',  'PubsCatalog',
'writer_id_index'
sp_fulltext_table  'books',  'create',  'PubsCatalog', 'isbn_index'

    有关更多信息，请参见 sp_fulltext_table。

    两个表都使用 PubsCatalog 全文目录。这些存储过程调用创建有关这两个全文索引的元数据。

  6. 为每个表指定要支持全文查询的列的名称，方法是对每列分别执行一次下列存储过程：
    sp_fulltext_column  'writers',  'organization',  'add'
sp_fulltext_column  'writers',  'bio',  'add'
sp_fulltext_column  'books',  'writer_name',  'add'
sp_fulltext_column  'books',  'the words',  'add'
sp_fulltext_column  'books',  'abstract',  'add'

    有关更多信息，请参见 sp_fulltext_column。

    说明 为了说明起见，故意犯了如下错误：对于 books 表来说，注册的是 writer_name 列，而不是 titles 列。

    这些存储过程调用增加有关两个全文索引的元数据。

  7. 为这些表创建全文索引，方法是对每个表分别执行一次下列存储过程：
    sp_fulltext_table  'writers',  'activate'
sp_fulltext_table  'books',  'activate'

    这样做实际上并不创建全文索引，而是在全文目录中注册这些表，使来自这些表的数据将被包含在下一次填充中。

  8. 启动对 PubsCatalog 全文目录的完全填充，方法是执行下列存储过程：
    sp_fulltext_catalog  'PubsCatalog',  'start_full'

    因为全文目录的填充是一种异步操作，所以不大可能立即创建出全文索引。

  9. 执行下列语句以验证 PubsCatalog 全文目录填充的进度：
    SELECT FulltextCatalogProperty ( 'PubsCatalog',  'PopulateStatus')

    有关更多信息，请参见 FULLTEXTCATALOGPROPERTY。

    如果全文目录的服务空闲并因此完成填充，则返回 0；否则返回 1 或更大的值以表示填充阶段。

  10. 发出 Transact-SQL 查询以确认管理任务执行无误。例如：
    SELECT B.writer_name, B.pub_date, B.the_words, A.royalties_ytd
FROM writers A,  books B
WHERE A.writer_name = B.writer_name
AND A.citizenship = 'Canadian'
AND CONTAINS (B.the_words, '"Indexing Service" NEAR "Indexing Service"')

SELECT writer_name, pub_date, abstract
FROM books
WHERE CONTAINS (title, '"Classic" NEAR "French" NEAR "Cooking"')

    最后一条查询结果是错误的，因为没有为全文查询启用 title 列。

  11. 执行下列语句以检查错误：
    SELECT ColumnProperty ( ObjectId('books'), 'title',  'IsFullTextIndexed' )

    有关更多信息，请参见 COLUMNPROPERTY。

    如果 title 列是 books 表的全文索引的一部分，该语句将返回 1，否则返回 0。

  12. 通过执行下列存储过程，列出参与对 books 表进行全文处理的列：
    sp_help_fulltext_columns  'books'

    有关更多信息，请参见 sp_help_fulltext_columns。

    说明 此次查询的结果显示存在一个错误，并且 writer_name 列而非 title 列包含在全文索引的定义中。

  13. 停用 books 表以便将 title 列添加到全文索引，同时将 writer_name 列删除，方法是执行下列存储过程：
    sp_fulltext_table  'books',  'deactivate'

    除了允许添加和删除列外还停用 books 表，这意味着该表不再参与 PubsCatalog 全文目录的填充。但将保留元数据，并且可重新激活该表。books 表的现有全文索引将一直保留到下一次对 PubsCatalog 全文目录进行完全填充，但在此期间并不使用，因为 Microsoft® SQL Server™ 2000 禁止对已停用的表进行查询。

  14. 在 books 表的全文索引的元数据中添加 title 列和删除 writer_name 列。对每个列执行一次下列存储过程：
    sp_fulltext_column  'books',  'writer_name',  'drop'
sp_fulltext_column  'books',  'title',  'add'

    有关更多信息，请参见 sp_fulltext_column。

  15. 使用下列存储过程重新激活 books 表：
    sp_fulltext_table  'books',  'activate'

    如果重新激活表但没有重新填充索引，则旧索引仍可用于对其余的全文启用列执行的查询，但不能用于对任何新的全文启用列执行的查询。在重写前，可以通过键入星号 (*) 作为列名在特定的查询上对来自已删除列的数据进行匹配，这些查询指定对所有全文列的检索。

  16. 执行下列存储过程以启动对 PubsCatalog 全文目录的增量填充：
    sp_fulltext_catalog  'PubsCatalog',  'start_incremental'

    增量填充可以刷新全文目录，其方式为对具有下列特征的启用全文的列中的数据进行索引：

    • 从上次填充以来曾被更新或插入的行。

    • 有 timestamp 列的表。

    • 从上次填充以来已为全文处理而启用的所有行，或其架构从上次填充以来曾被修改过（无论以何种方式）的列。
  17. 对 PubsCatalog 全文目录的重写完成后，从步骤 10 开始重新发出 Transact-SQL 查询。这一次应该不会发生错误。


对全文目录执行调查和清除任务

在本示例中，将执行典型的调查和清除任务。假设您已连接到 pubs 数据库，全文服务已经启动，并且正在处理虚构的 writers 表和 books 表。若要获得对这些表的描述，请参见使用存储过程和标量函数管理全文功能_administering_full-text_features_using_stored_procedures_and_scalar_functions。

  1. 通过执行下列存储过程，以获取一份已链接到 pubs 数据库的所有全文目录的列表：
    sp_help_fulltext_catalogs

    有关更多信息，请参见 sp_help_fulltext_catalogs。

    因为 pubs 数据库是当前数据库，该存储过程将为链接到 pubs 数据库的所有全文目录返回下列元数据：

    • 全文目录的名称和整数标识符

    • 全文目录根目录

    • 全文目录填充状态

    • 链接到此全文目录的表的数目

    与上述存储过程稍有不同的另一个存储过程（在其中指定了全文目录的名称参数）可以为单个全文目录返回这方面的信息。

  2. 执行下列存储过程，以获取一份已为全文处理而启用的数据库中所有表的列表：

    3. sp_help_fulltext_tables

    有关更多信息，请参见 sp_help_fulltext_tables。

    此存储过程为每个表返回下列元数据：

    • 由两部分组成的表名

    • 用作表的全文键的列的整数标识符

    • 用于对全文键列施加唯一约束的索引的名称

    • 表的全文状态

    • 表的全文目录名

    支持与此存储过程稍有不同的其它两种存储过程。如果指定了 fulltext_catalog_name 参数，将为与该全文目录链接的所有表返回这方面的信息。如果同时指定了 catalog_name 参数和 table_name 参数，或者仅指定了 table_name 参数，则为该表返回这些信息。

  3. 执行下列存储过程，以获取一份已为全文处理而启用的数据库中所有表的列表：
    sp_help_fulltext_columns

    有关更多信息，请参见 sp_help_fulltext_columns。

    此存储过程将为每个列返回下列元数据：

    • 列中由两部分组成的表名

    • 列的名称和整数标识符

    与此存储过程稍有不同的一种存储过程（在其中指定了表名参数）可以为单个表返回这方面的信息。

    经过编译的列表会指出一些问题。除 mytable 表外不再有任何表使用 mycatalog 全文目录，而 mytable 表不再有任何可被查询的全文列。

  4. 执行下列存储过程，以取消将 mytable 表注册为用于全文处理：
    sp_fulltext_table  'MyTable', 'drop'

    有关更多信息，请参见 sp_fulltext_table。

    这将除去该表有关全文索引的元数据。直至下一次完全填充或除去全文目录之前，现有的全文索引将保持原样。但它也保持为不使用状态。有关更多信息，请参见 sp_fulltext_table。

  5. 通过执行下列存储过程，从文件系统中除去 mycatalog 全文目录并从系统表中除去其元数据：
    sp_fulltext_catalog  'MyCatalogue', 'drop'

    有关更多信息，请参见 sp_fulltext_catalog。

    必须完成步骤 4 才能除去全文目录，因为必须更新其文本目录的元数据才能删除所有全文索引。

    在文件系统中至少存在一个不再有相应的 SQL Server 元数据的全文目录。这种现象的原因通常是因为删除了某个数据库。

  6. 通过执行下列存储过程，从文件系统中删除所有在 SQL Server 中不再有元数据的全文目录：
    sp_fulltext_service  'Clean_Up'

    有关更多信息，请参见 sp_fulltext_service。

    MixedUpCtlg 全文目录的结构与当前在 SQL Server 中为它记录的元数据不匹配。当全文目录正在被除去，或者数据库正在被除去并且 Microsoft 搜索服务不在运行时，就可能发生这种情况。除去操作会更改与全文目录相关的元数据，但无法完成该操作，因为 Microsoft 搜索服务没有运行。这将导致 SQL Server 中的全文元数据与文件系统中相关的物理全文目录之间不一致。通过唤醒调用 sp_fulltext_service 上的清除操作可以纠正这种不一致。（Microsoft 搜索服务必须正在运行。）

  7. 执行下列存储过程，以重建（但不重新填充）MixedUpCtlg 全文目录：
    sp_fulltext_catalog  'MixedUpCtlg',  'Rebuild'

    有关更多信息，请参见 sp_fulltext_catalog。

    带有 ENABLE 选项的 sp_fulltext_database 存储过程可以用于重建所有已知的全文目录。

  8. 执行下列存储过程，以启动对 MixedUpCtlg 全文目录的完全填充：
    sp_fulltext_catalog  'MixedUpCtlg',  'start_full'

说明 可根据需要创建、除去和修改全文目录；但是应避免同时在多个目录上对架构进行更改。


执行不常用的任务

这一方案中的示例说明了通常比其它管理任务更少用到的全文索引任务。

更改用于全文索引和检索的资源量

确定是否已将资源使用级别分配给全文服务，以及在请求填充后全文服务必须等待多长时间。

  1. 启动服务器后，执行下列语句：
    SELECT FulltextServiceProperty ( 'IsFullTextInstalled' )

    有关更多信息，请参见 FULLTEXTSERVICEPROPERTY。

    如果服务已经安装，该语句返回 1，否则返回 0。

  2. 通过执行下列语句确定已分配给全文服务的资源使用级别：
    SELECT FulltextServiceProperty ( 'ResourceUsage' )

    该语句将返回一个从 1（后台）到 5（专用）之间的值。

  3. 若要确定在请求填充全文索引后，全文服务必须等待多长时间才能初始化，请执行下面的语句：
    SELECT FulltextServiceProperty ( 'ConnectTimeout' )

    该语句将返回当前超时值。

  4. 因为这种安装 Microsoft® SQL Server™ 2000 的方式负荷很重，可以通过执行下列存储过程将初始化超时值从默认的 20 秒增加到 90 秒：
    sp_fulltext_service  'ConnectTimeout',  90

    有关更多信息，请参见 sp_fulltext_service。

询查全文键列

确定 pubs 数据库中 magazines 表上的普通 SQL Server mag_id_index 索引是否用于强制执行全文键列的唯一性。

  1. 执行下列语句：
    SELECT IndexProperty ( Object_Id('magazines', 'mag_id_index',  'IsFulltextKey' )

    有关更多信息，请参见 INDEXPROPERTY。

    如果索引确实是用于强制执行全文键列的唯一性，该语句将返回值 1，否则返回 0。

  2. 执行下列语句，以在 books 表中查找全文键列的名称：
    SELECT ObjectProperty(Object_Id('books', 'TableFulltextKeyColumn'))

    有关更多信息，请参见 OBJECTPROPERTY。


维护全文索引

维护全文索引有三种方式：

  • 完全重建

    重新扫描所有行。彻底重建全文索引。既可以立即执行完全重建，也可以通过 SQL Server 代理按调度进行。

  • 基于时间戳的增量重建

    重新扫描那些从上一次完全重建或增量重建以来曾更改过的行。这样做需要在表上有一 timestamp 列。不更新时间戳的更改（如 WRITETEXT 和 UPDATETEXT）是检测不到的。可以立即执行增量重建，也可以按调度进行。

  • 更改跟踪

    维护一份对索引数据的全部更改的列表。用 WRITETEXT 和 UPDATETEXT 进行的更改是检测不到的。可以用这些更改立即更新全文索引，也可以按调度进行，或者使用后台更新索引选项在更改一发生时便更新。

所使用的方法取决于许多因素，如 CPU 和可用的内存、数据更改的数量和速度、可用磁盘空间的大小，以及当前全文索引的重要性等。以下建议可作为选择维护方式时的参考。

  • 当 CPU 和内存不成问题，最新索引的值很高，且即时传播可以跟得上更改的速度时，使用带后台更新索引选项的更改跟踪。

  • 当 CPU 和内存可以在调度时间使用，用于存储更改的磁盘空间足够大，且调度时间之间的变化并没有大到使传播所需的时间比完全重建更长时，使用带调度传播的更改跟踪。

  • 如果大部分记录的更改或添加是立即发生的，应该使用完全重建。如果大部分记录是在扩展的时间段更改的，考虑使用带调度或后台更新索引的更改跟踪。

  • 如果每一次更改的文档数目很多（并不是所占的百分比很高），可以使用增量重建。如果大量记录的更改是在扩展时间段发生的，考虑使用带调度或后台更新索引的更改跟踪。

请参见

OBJECTPROPERTY

sp_fulltext_table


筛选支持的文件类型

当 image 列中的一个单元包含某种特定类型的文档时，全文检索将使用筛选来解释二进制数据。筛选会从该文档析取文本化信息然后提交，用于进行索引和后续查询。

Microsoft® SQL Server™ 2000 包含用于下列文件扩展名的筛选：.doc、.xls、.ppt、.txt 和 .htm。

许多文档类型可以存储在单个 image 列中。对于每种文档，SQL Server 根据文件扩展名选择正确的筛选。因为当文件存储在 image 列中时，文件扩展名是不可见的，所以扩展名必须另外存储在表上单独的列中。这一类型列可以是任何基于字符的数据类型，并包含文档的文件扩展名，如用于 Microsoft Word 文档的 .doc。如果类型列为 NULL，则将该文档视为文本文件。

说明 若要建立全文索引，文档的大小必须小于 16 兆字节 (MB)，并且不能包含超过 256 千字节 (KB) 的筛选文本。

文档类型列可通过下列方式创建：

在全文索引向导中，如果选择了一个 image 列用于索引，还必须同时指定一个 Binding column 列以存放文档类型。

sp_fulltext_column 存储过程也接受列的参数以容纳文档类型。

若要查看文档类型，请使用 sp_help_fulltext_columns 存储过程返回列名和列 ID。

一旦 image 列被编入索引，即可使用检索谓词 CONTAINS 和 FREETEXT 对其进行查询。

说明 筛选有可能处理嵌入父对象中的对象，这取决于其执行方式。筛选并不跟踪到其它对象的链接。

可以创建自定义筛选用于其它文件类型的全文索引。有关创建自定义筛选的更多信息，请在 Microsoft Web 站点上 MSDN Library 的 Platform SDK 部分中搜索"custom filters"。

请参见

sp_fulltext_column

sp_help_fulltext_columns


列级语言分析

语言分析是在所有全文索引数据上执行的。语言分析包括查找文字边界（断字）和组合动词（词干分隔）。分析的规则随语言的不同而异，可以为每个全文索引列指定不同的语言。Microsoft® SQL Server™ 2000 包含用于下列区域设置的语言分析包。

中性 | 德国
简体中文 | 意大利
繁体中文 | 日语
荷兰语 | 朝鲜语
英国英语 | 现代西班牙语
美国英语 | 普通瑞典语
法语 |

当某个列包含多个语言的数据或不支持的语言的数据时，请使用"中性"设置。

在全文查询函数 CONTAINS、FREETEXT、CONTAINSTABLE 和 FREETEXTTABLE 的参数上执行的语言分析取决于正在被查询的全文索引列的语言。如果未给列指定语言，默认语言是配置选项 default full-text language 的值。

对于本地化版本的 SQL Server，如果存在适当的匹配项，则 SQL Server 安装程序将 default full-text language 选项设置为服务器的语言。对于非本地化版本的 SQL Server 来说，default full-text language 选项默认值为"中性"。

说明 在单个全文查询函数子句中列出的所有列都必须使用相同的语言。

default full-text language 配置选项取代了 SQL Server 7.0 版中的 language neutral full-text 选项。在从 SQL Server 7.0 升级到 SQL Server 2000 时，根据 Unicode locale id 和 language neutral full-text language 选项的值来设置 default full-text language 的值。

请参见

sp_fulltext_column


对全文目录执行调查和清除任务

在本示例中，将执行典型的调查和清除任务。假设您已连接到 pubs 数据库，全文服务已经启动，并且正在处理虚构的 writers 表和 books 表。若要获得对这些表的描述，请参见使用存储过程和标量函数管理全文功能_administering_full-text_features_using_stored_procedures_and_scalar_functions

  1. 通过执行下列存储过程，以获取一份已链接到 pubs 数据库的所有全文目录的列表：
    sp_help_fulltext_catalogs

    有关更多信息，请参见 sp_help_fulltext_catalogs

    因为 pubs 数据库是当前数据库，该存储过程将为链接到 pubs 数据库的所有全文目录返回下列元数据：

    • 全文目录的名称和整数标识符

    • 全文目录根目录

    • 全文目录填充状态

    • 链接到此全文目录的表的数目

      • 与上述存储过程稍有不同的另一个存储过程（在其中指定了全文目录的名称参数）可以为单个全文目录返回这方面的信息。

    2. 执行下列存储过程，以获取一份已为全文处理而启用的数据库中所有表的列表：

      sp_help_fulltext_tables

      有关更多信息，请参见 sp_help_fulltext_tables。

      此存储过程为每个表返回下列元数据：

      • 由两部分组成的表名

      • 用作表的全文键的列的整数标识符

      • 用于对全文键列施加唯一约束的索引的名称

      • 表的全文状态

      • 表的全文目录名

        支持与此存储过程稍有不同的其它两种存储过程。如果指定了 fulltext_catalog_name 参数，将为与该全文目录链接的所有表返回这方面的信息。如果同时指定了 catalog_name 参数和 table_name 参数，或者仅指定了 table_name 参数，则为该表返回这些信息。

      3. 执行下列存储过程，以获取一份已为全文处理而启用的数据库中所有表的列表：
        sp_help_fulltext_columns

        有关更多信息，请参见 sp_help_fulltext_columns。

        此存储过程将为每个列返回下列元数据：

        • 列中由两部分组成的表名

        • 列的名称和整数标识符

        与此存储过程稍有不同的一种存储过程（在其中指定了表名参数）可以为单个表返回这方面的信息。

        经过编译的列表会指出一些问题。除 mytable 表外不再有任何表使用 mycatalog 全文目录，而 mytable 表不再有任何可被查询的全文列。

      4. 执行下列存储过程，以取消将 mytable 表注册为用于全文处理：
        sp_fulltext_table  'MyTable', 'drop'

        有关更多信息，请参见 sp_fulltext_table。

        这将除去该表有关全文索引的元数据。直至下一次完全填充或除去全文目录之前，现有的全文索引将保持原样。但它也保持为不使用状态。有关更多信息，请参见 sp_fulltext_table。

      5. 通过执行下列存储过程，从文件系统中除去 mycatalog 全文目录并从系统表中除去其元数据：
        sp_fulltext_catalog  'MyCatalogue', 'drop'

        有关更多信息，请参见 sp_fulltext_catalog。

        必须完成步骤 4 才能除去全文目录，因为必须更新其文本目录的元数据才能删除所有全文索引。

        在文件系统中至少存在一个不再有相应的 SQL Server 元数据的全文目录。这种现象的原因通常是因为删除了某个数据库。

      6. 通过执行下列存储过程，从文件系统中删除所有在 SQL Server 中不再有元数据的全文目录：
        sp_fulltext_service  'Clean_Up'

        有关更多信息，请参见 sp_fulltext_service。

        MixedUpCtlg 全文目录的结构与当前在 SQL Server 中为它记录的元数据不匹配。当全文目录正在被除去，或者数据库正在被除去并且 Microsoft 搜索服务不在运行时，就可能发生这种情况。除去操作会更改与全文目录相关的元数据，但无法完成该操作，因为 Microsoft 搜索服务没有运行。这将导致 SQL Server 中的全文元数据与文件系统中相关的物理全文目录之间不一致。通过唤醒调用 sp_fulltext_service 上的清除操作可以纠正这种不一致。（Microsoft 搜索服务必须正在运行。）

      7. 执行下列存储过程，以重建（但不重新填充）MixedUpCtlg 全文目录：
        sp_fulltext_catalog  'MixedUpCtlg',  'Rebuild'

        有关更多信息，请参见 sp_fulltext_catalog。

        带有 ENABLE 选项的 sp_fulltext_database 存储过程可以用于重建所有已知的全文目录。

      8. 执行下列存储过程，以启动对 MixedUpCtlg 全文目录的完全填充：
        sp_fulltext_catalog  'MixedUpCtlg',  'start_full'

      说明 可根据需要创建、除去和修改全文目录；但是应避免同时在多个目录上对架构进行更改。