配色: 字号:
SQLServer基础学习教程总结
2022-10-24 | 阅:  转:  |  分享 
  
返回总 返回总 返回总 返回总 目录 目录 目录 目录
目 目 目 目 录 录 录 录
第 1 章 Transact-SQL DDL..............................................................5
1.1 创建数据库.............................................................................................5
1.2 创 建 表.............................................................................................8
1.3 表 的 列...........................................................................................11
1.4 .........................................................................................18
表 的 约 束
1.5 创 建 视 图.........................................................................................22
1.6 视 图 分 类.........................................................................................25
1.7 创建视图选项.......................................................................................27
1.8 创建视图举例.......................................................................................28
1.9 .........................................................................................32
创 建 索 引
1.10 删除数据库 表 视图和索引.........................................................34
1.11 小 结.............................................................................................34
第 2 章 Transact-SQL DML 详解 .................................................. 36
2.1 Select ............................................................................................36
语句
2.2 Select 的子句 ........................................................................................42
2.3 复杂的 Select 语句 ...............................................................................46
2.4 Select 的条件 ........................................................................................51
2.5 Select 的其他用法 ................................................................................59
2.6 Insert .............................................................................................67
语句
2.7 Update 语句 ..........................................................................................69
2.8 Delete 和 Truncate Table 语句 .............................................................72
2.9 并行修改和表锁...................................................................................73
2.10 存 储 过 程.......................................................................................75
2.11 .....................................................................................79
存储过程参数
2.12 存储过程编程技巧.............................................................................81
2.13 游 标.............................................................................................84
2.14 存储过程错误处理.............................................................................89
2.15 触 发 器.........................................................................................90
2.16 .................................................................................96
触发器编程技巧
2.17 小 结.................................................................................................99
第 3 章 SQL Server 与 其他产 品集成 概述 ................................. 101
3.1 SQL Server 与 Access 的集成...........................................................101
3.2 SQL Server 与 Excel 的集成..............................................................1063.3 在 IIS 和 IE 中使用 SQL Server ........................................................106
3.4 SQL Server 与 Microsoft Transaction Server 集成............................107
3.5 .............................................................................................111
小 结
第 4 章 使用 Access 访问 SQL Server ........................................ 112
4.1 概 述.............................................................................................112
4.2 Access 连接到 SQL Server.................................................................115
4.3 Access ........................................................................125
设计 应用程序
4.4 开发 VBA 应用程序 ..........................................................................134
4.5 编 程 心 得.......................................................................................141
第 5 章 使用 ODBC 访问 SQL Server 数据 库 ........................... 143
5.1 ODBC .........................................................................................143
概述
5.2 ODBC 组成.........................................................................................144
5.3 ODBC 应用.........................................................................................146
5.4 ODBC 调用的前期和后期工作.........................................................153
5.5 通过 ODBC 访问 SQL Server 数据库...............................................158
5.6 ODBC SQL Server ...............................................163
通过 修改 数据库
5.7 通过 ODBC 调用 SQL Server 数据库的存储过程...........................167
5.8 ODBC 错误处理.................................................................................169
5.9 小 结.............................................................................................170
第 6 章 使用 ADO 访问 SQL Server 数据 库.............................. 171
6.1 概 述.............................................................................................171
6.2 使用 ADO 连接到 SQL Server ..........................................................177
6.3 使用 Recordset 对象查询 SQL Server 数据库..................................181
6.4 使用 Recordset 对象修改 SQL Server 数据库..................................189
6.5 Command SQL Server .................................193
使用 对象操纵 数据库
6.6 高 级 应 用.......................................................................................201
6.7 错 误 处 理.......................................................................................210
6.8 小 结.............................................................................................211
第 7 章 使用 SQL-DMO 管理 SQL Server ................................. 212
7.1 概 述.............................................................................................212
7.2 SQL-DMO 的核心对象分层结构......................................................214
7.3 SQL-DMO 应用初步..........................................................................219
7.4 使用 SQL-DMO 管理 SQL Server 服务器........................................224
7.5 .............................................................................................232
小 结
第 8 章 在 C 中使 用 嵌入 SQL 访问 SQL Server 数据 库.......... 233
8.l 嵌入式 SQL 应用程序开发环境 ........................................................233
8.2 嵌入式 SQL 数据类型 .......................................................................234
8.3 SQL ...............................................................................235
嵌入式 语法8.4 嵌入式 SQL 数据库访问过程 ...........................................................242
8.5 SQL 命令执行方式 ............................................................................248
8.6 SQL .......................................................................252
嵌入式 选项设置
8.7 建立嵌入式 SQL 应用程序 ...............................................................252
8.8 小 结...............................................................................................257第一部分 Transact-SQL 编程
本部分主要讨论 Microsoft SQL Server 的 Transact-SQL 程序设计技术
Transact-SQL 是结构化 查询语言(SQL) 的扩展 而 SQL 是关系数 据库语言的 工
业标 准 SQL 语言分两种 一是用于定义关系数据库的数据 叫做数据定义语
DDL DML
言 二是 用于操纵关系 数据库中的数据 叫做数据操 纵语言
本部分将分别讨论 DDL 和 DML 设计技术第1章 Transact-SQL DDL
在 Transact-SQL 语言中 DDL 数据 定义 语言 是基础 因为 它负 责各 类数 据库 对象
的创建 和删 除 本 章主 要 介绍下 列四 个基 本 DDL 语句 Create Database Create Table Create
View 和 Create Index 以及 删除 这些 语句 创建 的对 象 Drop Database Drop Table Drop View
和 Drop Index
1. 1 创建数 据库
在 SQL 术语中 数据 库是一 个 容器 包含 了相 关的 基 表 视图 索引 存储 过程 和其
他对象 在创建这些对 象之前 必须 有一个存储它们 的数据库 在数据库 中 对 象被进 一
步组织为 有一个所 有者 对于某些类型的对象 例如 表 只要属于不同的用户 就可以 在
同一个数 据库中有同样 的名称 然而 对于 产品系统 最好不要出现重复的 名称 一般情 况
下 大多 数产 品对 象由 数 据库所 有者 拥有
为了创 建数 据库 用户 必 须是系 统管 理员 或者 被授 权 使用 Create Database 语句 Create
Database 命令 最简 单的 形式 如下
Create Database AppDta
这条语 句创 建 AppDta 数据库 并且把 SQL Server 的 model 数据 库定 义复制到新数 据
库中 也就是说 model 数据库中的每一个表 视图 存储过程 等等的 空拷贝都 复制在 新
数据库 中创 建 SQL Server 为这个数据 库创 建两 个 NT Server 文件 appdta.mdf 保存数 据
appdta_log.ldf 保存 事务 日志 的内 容 这两个文 件 的 默认初 始 大小分 别设 置为 model 数据库
的主文 件和 日志 文件 的大小 如果 需要 SQL Server 将自动扩 展这 些文 件
1.1.1 指定 位置 和大 小
如果 希望为数 据库或事 务日志指 定 一 个或者多 个 特 定 文 件 增加 一 个 On 子句 列出
一个或 者多 个文 件 并 可 为分配 这个 文件 的空 间( 以 MB 为单 位)指 定一个可 选值 例 1-1 说
明了创 建数 据库 的基 本语句
例 1-1
Create Database AppDta
On Primary
( Name = AppDta1,
Filename = `c: production data appdta1.mdf'',
Size = 10MB,
MaxSize = 100MB,
FileGrowth = 10MB),
( Name = AppDta2,
Filename = `c: production data appdtal.ndf'',
Size = 10MB,
FileGrowth = 10MB )
On 关键 字之 后的 第一 项应该 指 定可 选的 Primary 关键字 表明 这是 一个 主文 件 它包
含该数据 库的系统表和 初始化信息 指定的 其他从属 文件用于保存在用户 表和其 他对象 中
的数据 在这 个示 例中 的 参数如 下
Name SQL Server 使用的逻 辑名 称
Filename 完全限 定的 NT Server 文件 名
Size 文件 的初 始大 小 默认 值是 model 数据 库主 文件(model.mdf) 的大小
MaxSize 最大 的文 件尺 寸 默认 值是 占满 整个 空间
FileGrowth 当需要 时 SQL Server 扩展 文件 的量 默认值 是 10%
一个 SQL Server 数据库 可以 超过 一百 万 TB 大小 1TB=1000MB
为了提 高性 能和 可恢 复性 可以 使用 Log On 子句来 指定 数据 库的 SQL Server 将事务
日志存 储在 一个 与数 据库对 象 不同 的设 备上 如例 1-2
例 1-2
Create Database AppDta
On Primary
( Name = AppDta1,
Filename = C:\Production\date\appdtal.mdf ,
Size = 10MB,
MaxSize = 100MB,
FileGrowth = 10MB),
( Name = AppDta2,
Filename = ‘C:\Production\date\appdtal.mdf’,
Size = 10MB,
FileGrowth = 10MB)
Log On
( Name = AppDtaLog1,
Filename = ’d:\production\log\appdtalog1.ldf''’,
Size = 10MB,
MaxSize = 100MB,
FileGrowth = 10MB )
这种 方 式 如 果 数 据 库 所在的 物 理设备被破 坏 日 志还可 以 使 用( 如 果 该日志 所在 的设
备 没 有被 破坏) 使用一个 以 前的数据库备份和一个未 破 坏的日志的脱机拷贝 可以将数据
库恢复 到保 存数 据库 恢复的 设 备失 败时 的状 态 当 指 定明确 的文 件时 按照 SQL Server 的
约定 使用.mdf 作 为 数据 库 主 文件的扩 展名 .ndf 作为 数 据库从属文件 的 扩 展名 .ldf 作为
事务日 志文 件的 扩展 名
1.1.2 修改 数据 库
在数据 库创 建之 后 可以使 用 Alter Database 语句增 加新 文件 删除已 有的文 件 或修改
文件的 设置 例 1-3 的语句 可 增加 一个新 文件
例 1-3
Alter Database AppDta
Add File ( Name = AppDta3,
Fileanme = `c:\production\data\appdta3.ndf ,
Size = 10MB,
FileGrowth = 10MB )
还有一 个 Add Log File 子句 它与 Add File 子句有 相同 的格 式 Remove File 子句只使
用以前 指定 的逻 辑文 件名
Alter Database AppDta
Remove File AppDta2
Modify File 子句需 要以 前指 定的 逻辑文 件名 并且 可 以带任 何其 他的 参数 例如 参
数 File Growth:
Alter Database AppDta
Modify File
( Name = AppDta1,
FileGrowth=50MB )
1.1.3 定义 文件 组
数据库文件 不包括事务日志文件 可以组成文件组 最初创 建 一个数据库时 该数
据库的默 认文件组包含 了主文件和没 有明确 分配给用 户定义 文件组的从属 文件 在许多 情
况下 默认 的 文 件组 已经足够 了 对于有些 系统 在 指定的 设备 上创 建用 户定 义 的文件 组
可以提高 数据库性能或 可恢复性 因 为可以指定表或 索引所在的文件组 所以 文 件组提 供
了一种间 接手段 可以 把表和索引放 在指定的设备上 另外 当使用一个 包含了 许多文 件
的文件 组时 SQL Server 根 据文件 可用 的自 由空间 把 文 件组中的 数 据 按 比例 散布在文 件
中 下面 是一 个创 建文 件 组的示 例
Alter Database AppDta
Add FileGrouup AppDtaGroup1
当创建 一个 文件 组之 后 可以使 用 Alter Database 语句把新文 件 增加到该 文 件 组中 对
于那些增 加到用户 定义的文件组中的 文件 一般应该 指定一个 与该数据库主文件 不同的 设
备 如例 1-4
例 1-4
Alter Database AppDta
Add File
( Name = AppDta2,
Filename = d:\production\data\apppdta2.ndf ,
Size = 10MB,
MaxSize = 100MB,
FileGrowth = 10MB),
( Name = AppDta3,
Filename = d:\production\data\appdta3.ndf ,
Size = 10MB,
MaxSize = 100MB,
FileGrowth = 10MB)
To FileGroup AppDtaGroup1
为了删 除文 件组 和文 件组所 包 含的 文件 可以 使用 类 似下面 的语 句
Alter Database AppDta
Remove File AppDta2
Alter Database AppDta
Remove File AppDta3
Alter Database AppDta
Remove FileGroup AppDtaGroup1
在删除 文件 或者 文件 组时 它 们必须为空
也可以 使用 Alter Database 语句改 变某 个数 据库 的默 认文 件组 例如 下面 的语 句
Alter Database AppData
Modify FileGroup AppDtaGroupl Default
1.2 创 建 表
在关系 数据 库中 基表 包 含了实 际的 数据 在一 个 SQL Server 数 据库中 可以 创建 多
达两万 亿个 表 为了 用 SQL 创建表 输入 一条 Create Table 语句 指定 下述内容
包含表 的数 据库
表的所 有者
表名 在同一 个数据库中和同一个所有者下 该表名必须与任何其它基表或视图
不同
指定 1 到 1024 个列
主键约 束(可选)
1 到 250 个 Unique 约束( 可选)
1 到 253 个外 键约 束( 可选)
1 个或 者多 个 Check 约束 限制 插入 表中 的数 据(可选)
存储表 的文 件组( 可选)
1.2. 1 创建 表的 一般 要求
例 1-5 示意 了一 个比 较复杂 的 Customer 表的 Create Table 语句
正如例 1-5 所示 Create Table 语句首 先指 定将 要创 建 的表 然后 列出 列和 约束 的定 义
之间由逗号 分 开 用一 组 括号括 住 SQL 是一 种 自由 格 式的语 言 一条语句可以放 在多 行
上 在字 和符号之间使 用空格 以提高可读性 该示 例说明 代码样式 代码以每 个列的定
义开始 约束 单独 占一 行 每 个列 的定义中 相似 的部 分对齐 虽然 这种列样式不是必须 的
但是它 比不 对齐 的文 本流形 式 更容 易理 解
SQL 关键字 对大 小写 不敏感 Create Table CREATE TABLE 和 CrEaTe TaBLE 都是正
确的 然而 记住 在安 装 SQL Server 时选 择的 排列 顺序 决定 标识 符和 字符串 文字 是否对大
小写敏 感 如 果使 用对 大 小写不 敏感 的排 列顺 序(默认 的安装 选项) 安装 SQL Server 那么 SQL
Server 认为表 名 Customer customer 和 CUSTOMER 是 相同的 并且‵x 等于‵x 如
果使用 对大 小写 敏感 的排列 顺 序安 装 SQL Server 那么 SQL Server 把表 名Customer customer
和 CUSTOMER 作为 不同 的标 识符 并且 认为‵x 不等 于 ‵X
注意 即 使使用对大小 写不敏感 的排列顺序 系统表 中的表名和其他 对象名 称也是 以输入它 们的 方式 存储 例如Customer customer 或CUSTOMER 因此 如果 在AppDta
数据库 中 显 示表 的 清 单 那么 例 1-5 所 示的 语 句创建的 表将被 列为 Customer,
使用对 大小 写 不 敏感 的排列顺 序 仍然可以 在 SQL 语句中 使用 CUSTOMER 或者
customer 引用 该表
例 1-5 中使用的表名是一种 完全限定的名称 在 未限定的 表名 Customer 之前包括
数据库 名称(AppDta)和 所 有者名 称 dbo 使 用 小圆点“.”作 为限定名称 的分 隔 符
当创建数 据库对象时 一般建议 在代码中明确 指定数 据库名称和所有 者名称 这样 可
以归类表 的数据库和所 有者 避免偶 然在错误的数据 库中或 者使用错误的 所有者 创建表
正如本 示例 所示 为了 创 建一个 由数 据库 所有 者拥 有 的表 可 以在限 定 的名 称中使用 dbo
如果省 略了 数据 库名 称 就会 在当前 数据 库中 创建 表 当 前数 据库可以是分 配 给 SQL Server
帐户的 数据 库 或者 是在 Use 语句中指 定的 数据 库 例 1-5 是 Customer 基表 的 Create Table
语句
例 1-5
create Table AppDta.dbo.Customer
( CustId Int Not Null
check(CustId>0),
Name Char(30) Not Null
check(Name<>''''),
ShipLine1 VarChar(100) Not Null
Default '''',
ShipLine2 VarChar(100) Not Null
Default '''',
ShipCity Char (30) Not Null
Default '''',
ShipState Char ( 2) Not Null
Default '''',
ShipPostalCode1 Char (10) Not Null
Default '''',
ShipPostalCode2 Char (10) Not Null
Default '''',
ShipCountry Char (30) Not Null
Default '''',
PhoneVoice Char (15) Not Null
Default '''',
PhoneFax Char (15) Not Null
Default '''',
Status Char ( 1) Not Null
Default '''',
CreditLimit Money( 1) Not Null
Check (
(CreditLimit Is Null) Or
(CreditLimit >=0));
EntryDateTime DateTime Not Null
Default Current_Timestamp,RowTimeStamp imeStamp Not Null,
Constraint CustomerPk Primary Key ( CustId),
Constraint CustomerStatus Check ( ( Status<>'' '')Or
(CreditLimit Is Null) ) )
Use AppDta
所有者 名称 也可 以省略 SQL Server 会用当 前 的 用户名 作 为所 有 者 完 全限 定的表名
和视图 名必 须是 唯一 的
SQL 对象名 称可 以长 达 128 个字符 包括 字母 数字和下述特殊 的 符 号 _( 下划线)
#( 井号) $( 美 元符号) 和@(at 号) 如果只使 用字母(A-Z) 开 头并且 只 包 含字 母 和数 字(0-9)
对于跨 系统 或者 跨国 应用程 序 可以 避免 可能 出现 的 命名问 题
另外 Transact-SQL 有多个保留 字 例如 Create Table 和 Order 它 们 有特殊的 含义
这些保 留字 列在 Microsoft SQL Server Transact-SQL 参考手 册中 的 保 留关键字 话题中
如果希 望使 用这 些保 留字作 为 表名 列名 或者 其他 SQL 对 象名称 那么当它们出现 在 SQL
语句中 时 必须 用双 引号括 起 这些 名称 下 面这 个示例 说 明如 何编写一 个 SQL Server 语句
从表名 为 Order 的表 中检索 行
Select
From Order
Where CustId = 499320
引号括 起的 名称 也可 以用于 包 含特 殊字 符的 名称 如 下示例
Select
From Order+Detail
Where CustId = 499320
一般应 避免 使用 SQL 的保留字 或者 特殊 的字 符作 为 SQL 对 象的名 称
注意 必须 使用 SQL Server 的 quoted identifier 用 户选项才 能 使 用引号标 识符
Transact-SQL 还允 许 使 用方 括号 作为 关 键字的 分 隔符 例如 Order 这种语
法不需 要引 号标 识符 用户选项
1.2.2 在指 定文 件组 上创 建表
正 如 在本 章前 面“ 定义文件组” 一节中讨论的那样 可以定义文件组 它是一个或 者多
个存储应 用程序数据的 操作系统文件 的集合 为了把 一个新 表放在用户定 义的文 件组上
可以在 Create Table 语句 的末 端增 加一 个 On 子句
Create Table AppDta.dbo.Customer
( CustId Int Not Null
Check ( CustId > 0 ),
Name Char( 30 ) Not Null
Check ( Name <> ‘ ’ )
...
Constraint CustomerStatus Check ( ( Status <. ‘ ’ ) Or
( CreditLimit Is Null ) ) )
On AppDtaGroup1
这个文 件组 必须 已由 Alter Database 语句创 建
注意 SQL 还有 一个 Create Schema 语句 用于将 多个 Create Table,CreateView 和Grant 语句 组合 到一 个 SQL 语句中 当处 理 Create Schema 语句 时 SQL Server
按序连 续创建对 象 以 便满 足 所 有的逻 辑依 赖 关 系(例如 视图 和外 键) 虽然
一般由 代码 生 成 器使 用 Create Schema 语句 但 是 该语 句 也可 以 用 于手工创 建
有从属 外键 的两 个或 者多个 表
1.3 表 的 列
在 Create Table 语句 中 在 新 表名之 后 可以 编码 1 到 1024 个 列定义 行的 最大长 度
是 8060 字节 包 括 内部 数据所需的 字 节 实际 中 每 行 最 大 的应 用程 序 数 据要低于 8060
字节 例如 有 10 个列 声明 为 Character 数 据类 型的表最多可以 保 存 8038 个 应 用数据字 节
每一个列定义指定列名和一种数据类型 有些数据类型有长度或者精度( 数 字总长)
另外 Decimal 和 Numeric 数据类 型有 小数(小数 点右 端的 位 数) 表 1-1 列出 了 SQL 列的数
据类型
表1- 1 SQL 列的数据类型
数据类型 描述
字符和文 字符和文 字符和文 字符和文 本 本 本 本
Char(length) 固定长度的字符串 长度从 1 到 8000 如果省略了长度 那
Character(length) 么默认值是 1
NChar(length) 固定长度的 Unicode 字符串 长度从 1 到 4000( 如果省略了
NCharacter(length) 长度 则默认长度是 1)
National Char(length)
National Character(length)
Char Varying(max-length) 变长度的字符串 最大长度从 1 到 8000 如果省略了 max-
Character Varying(max-length) length, 则默认长度是 1
VarChar(max-length)
nChar Varying(max-length) 变长度的 Unicode 字符串 最大长度从 1 到 4000 如果省略
nCharater Varying(max-length) 了 max-length, 则默认值是 1
nVarChar(max-length)
National Char Varying(max-length)
National Character Varying(max-length)
National VarChar(max-length)
Text 变长度字符数据 最多达到 2 147 483 647 字节 行中
存储指向第一个数据页的指针 实际的文本是以 b- 树数据 页
存储
nText 变长度的 Unicode 字符数据 最多可达 1 073 741 823
National Text 个字符( 或者 2 147 483 646 字节 行中存储指向第一
个数据页的指针 实际的文本是以 b- 树数据页存储
Dec(precision,scale) 数值 precision 是位数 范围是 1 到 38 scale 是小数点右边的
Decimal(precision,scale) 位数 范围是从 0 到 precision 指定的数字 可用 Decimal(p) 表
Numeric(precision,scale) 示 Decimal(p,0),也可以用 Decimal 自身表示 Decimal(18,0) 然
而 Decimal 使用明确的 Precision 有助于提高文档的清晰度 续表
数据类型 描述
注意 对于 Decimal 列 SQL Server 的最大默认 precision 是 28 使
用/p 命令行开关启动 SQL Server 可以设置该值为其他值
数字 数字
数字 数字
单字节 无符号 二进制整数 范围是 0 到 255
TinyInt
两字节二进制整数 范围是-32,768 到 32,767
SmallInt
四字节二进制整数 范围是-2,147, 483,648 到 2,147, 483, 647
IntInteger
八字节二进制整数 范围是-1.8E19 到 1.8E19
BigInt
浮点数 Mantissa-size-in-bits 是尾数的位数 范围是 1 到 53 1-24
Float(mantissa-size-in-bits)
指定单精度(4 字节) 25-53 指定双精度(8 字节)
注意 可以使用 Float 本身表示 Float(53)
等价于 Float(23) Real 列有 7 位数精度
Real
等价于 Float
Double Precision
货币 货币
货币 货币
四字节数字 小数点右面有四位数字 范围是-214 748.3648 到
SmallMoney
214 748.3647
八字 节数 字 小数 点右 面有 四 位数 字 范围 是从-922 337 203
Money
685 477.5808 到 922 337 203 685 477.5807
日期和时 日期和时 间 间
日期和时 日期和时 间 间
四字节日期和时间 日期范围是 1-1-1900 到 6-6-2079 时间精度是
SmallDateTime
自午夜开始的 1 分钟之内
八字节日期和时间 日期范围是 1-1-1753 到 11-31-9999 时间精
DateTime
度 3.33 毫秒之内
字节 字节 二 二 进制和 进制和 图像 图像
字节 字节 二 二 进制和 进制和 图像 图像
一位 数字 0 或者 1
Bit
固定长度二进制数据 长度从 1 到 8000 字节 如果省略 Legth, 默认
Binary(length)
值是 1
变长度二进制数据 最大长度从 1 到 8000 字节 如果省略 max-
BinaryVarying(max-length)
Legth 默认值是 1
Va r B i n a r y(max-length)
变长度二进制数据 最长为 2,147,483,647 字节 行中存储指向第一
Image
个数据页的指针 实际的数字以 b- 树数据页存储
系统类型 系统类型 系统类型 系统类型
等价于 nChar Varying(128)
SysName
唯一标识数字 存储为 16 字节的二进制值
UniqueIdentifier
当插入或者修改行时 由 SQL Server 指定的唯一的 8 字节
TimeStamp
时间序列值 注意 名称是 TimeStamp 而无数据类型的列是
使用 TimeStamp 数据类型创建的
Cursor 允许在 存储过 程中创建 游标变 量 游 标允许一 次一行 地处理 数
Cursor
据 这个数据类型不能用作表中的列数据类型
可包含除text,ntext image, 和 timestamp 之外的其它任何数据类 型
Sql_variant
可用来 声明 T-SQL 语句的 变量 也可作 为自定 义函数 的返回 值 但 不
Table
能用来定义表的列在上述 所 有 的数据类 型 中 text ntext 和 image 是 较为特 殊的 三 个 由于 text ntext
和 image 型的数 据最 大可达 2GB 所以 在 7.0 及以前 的版 本中 用 text ntext 和 image 定
义的列 不能 在每 一行 中 直接存 储数 据 而只 能在 其 中存储 指针 由指 针指 向实际 存 储 text
ntext 和 image 型数据 的页面 而在 SQL Server 2000 中 情况有 所变 化 用户 可 以用系 统
存储过 程 sp_tableoption 的 开关选 项 text in row 来决 定是 否直 接在 行中 存储 数据
当 text in row 被设 置为 off 时 同 7.0 及以 前的 版本 一 样 只能 在其 中存 储指 针 指向
实际的 页面 而不 能存 数 据
当 text in row 被设 置为 on 时 就可 以在 其中 存储 较小的 text ntext 和 image 型数据
只有当 数据 超过 了一 行所允 许 的范 围时 才存 到单 独 的页面 文件 中去
1.3. 1 TimeStamp 列
无论何 时插 入或 者修 改一行 数 据时 都会 自动 修改 用 TimeStamp 数据 类型说明的列 或
名为 TimeStamp 但没有数据 类型 的列 SQL Server 设置 TimeStamp 列为下述值 保证该
值在一 个数 据库 中是 唯一的 并且 大于 以前 指定 的值 该 值的数 据 类型与 DateTime 数据类
型不同 永远 也不 能直 接 设置 TimeStamp 列的值
一个表 只能 有一 个 TimeStamp 列 可以使 用 TimeStamp 列的值 决定 自从 上次检索 之 后
是否有 其他 进程 修改 数据行 在应 用程 序中 检查 TimeStamp 列的值 提供 了一种 有 效的 方
法 可以 用这 种方 法在 防 止 与其他进 程的 修改 冲突 时 允许 并行 浏览 和修 改表
1.3.2 Identity 列
SQL Server 允许标识表中一个整数(TinyInt SmallInt Integer Decimal(p 0) 或者
Numeric(p 0)) 列作 为该 表的 Identity 列 为此 可以 在 列的数 据类 型之 后增 加 Identity 关
键字 如下 示例:
...,
RowId Integer Identity,...
当插入 一个 新行 时 SQL Server 自动为 identity 列分配一 个数 字 在默 认情 况下 对每
一个新 行 这个 数字 以 1 开始 增量 是 1 通 过 在关键 字 Identity 的后面指定 一个 或 者两 个
值 可以 设置 起始 值和 增 量值
RowId Integer Identity(10),...Start at 10,increment by 1
RowId Integer Identity(100,2),...Start at 100,increment by 2
注意 Identity 列不 允许 空(参 见 下一节)并 且不能 保 证唯一 性 除非 指定 一个 主键 约
束或者 唯一 约束 或者 为 该列创 建一 个唯 一索 引
1.3.3 行全 局唯 一标 识符 列
SQL Server 还允许在表中标记一个 UniqueIdentifier 列作为一个行全局唯一标识符
(GUID) 列 为此 可 以在列 的数 据类 型之 后增 加 RowGuidCol 关键 字 例如 下 面的例子
...
RowGuid UniqueIdentifier RowGuidCol,...
当插入 一个 新行 时 RowGuidCol 列不 能像 一个 Identity 列那样自动 修改 然而 可以
将 NewId 函数指 定为 列的默 认 值 以达到 类似 的效 果 RowGuid UniqueIdentifier
RowGuidCol
Not Null
Default NewId()
注意 Identity 列包 含了 一个 系统 生成 的 整数 一 般 用作主 键 一个 RowGuidCol 列
一般包 含一 个 NewId 函数的 值 主 要用于复 制或 者用于 需要一个在 所有系统 的所有 表的 所
有行中 有一 个唯 一标 识符
1.3.4 Sql_variant
Sql_variant 是 SQL Server 2000 的新 增数 据类 型 目前 在 ODBC 中 还没有完 全支 持这
种变量 因此 在用 Microsoft OLE DB Provider for ODBC (MSDASQL) 查询具有 Sql_variant
类型的列时 会返回二进制值 如 对包含字符串 ''PS2091'' 的列进行查询时会得到
0x505332303931 的返 回值
Sql_variant 在所有的数 据 类 型中 优 先 级最高 在进行比较或转 化时按表 1-2 的顺序进
行 Sql_variant 型的数 据进 行比 较时 按如 下规 则进 行
表1-2
优先级 数据族
sql_variant sql_variant
datetime datetime
smalldatetime datetime
float approximate number
real approximate number
decimal exact number
money exact number
smallmoney exact number
bigint exact number
int exact number
smallint exact number
tinyint exact number
bit exact number
nvarchar Unicode
nchar Unicode
varchar Unicode
char Unicode
varbinary binary
binary binary
uniqueidentifier uniqueidentifier
1. 当进行 比 较 的 sql_variant 值属于不同基 数 据 类型 而且 基数 据 类型属于 不 同 的数据
类型族 时 在表 1-2 中 族 优先级 高的 sql_variant 值的优 先级 就高
2. 当进行 比 较 的 sql_variant 值属于不同基 数 据 类型 而且 基数 据 类型又相 同 时 在表1-2 中基数据 类型 优先 级高的 sql_variant 值的优先 级就 高
3. 当 sql_variant 变量属 于 char, varchar, nchar 或 varchar 时 它们 按照 标准 LCID 进行
比较
1.3.5 Table 型数 据与 用户 自定 义函 数
SQL Server 2000 新增 了 Table 型数据 Table 型数据 不能 用来 定义 列的 类型 只能 用作
T-SQL 变量或者 作为 自定义 函 数的 返回 值 例 1-6 是一 个简 单的 table 型 数据的 例子
例 1-6
Declare @TableVar Table
(Cola int Primary Key,
Colb char(3))
Insert Into @TableVar Values (1, ''abc'')
Insert Into @TableVar Values (2, ''def'')
Select From @TableVar
Go
以上语 句定 义了 一个 名为 TableVar 有两 列的 table 型变 量 像通 常的 表一 样 table 型
数据也 有 insert select 等操 作
在 SQL Server 2000 中 table 型数 据与 用户 自定 义函 数 是密不 可分 的 SQL Server 2000
支持两 种类 型的 函数 内 置函数 和用 户定 义函 数 内 置函数 只允 许 T-SQL 语句调 用 而不
能更改 使 用 用 户定 义函数可 以根 据需要定 义自 己所 需 的 函数 用户 定义 函数 可 以带参 数
也可以 不带 参数 但只 能 返回单 值 正是 由于 这个 原 因 SQL Server 2000 增加了 table 型
数据 其值 可以 是整 型 字符 型或 数值 型 例 1-7 是 一个简 单的 用户 定义 函数 说明 了用
户定义 函数 的基 本结 构
例 1-7
Create Function CubicVolume
(@CubeLength decimal(4,1), @CubeWidth decimal(4,1),
@CubeHeight decimal(4,1) )
Returns decimal(12,3)
As
Begin
Return ( @CubeLength @CubeWidth @CubeHeight )
End
在例 1-7 中用 CREATE FUNCTION 创建 了一 个函 数 CubicVolume 来计算 立方 体的 体
积 变量 CubeLength CubeWidth CubeHeight 为输 入参 数 返回 值为 数值 型 BEGIN 表
明函数 体的 开始 END 表明 函数 体的 结束
通过例 1-8 我们 就会 清楚用 户 定义 函数与 table 型数 据 是如何 有机 结合 的
例 1-8
Use pubs
Go
Create Function SalesByStore (@storid varchar(30))
Returns Table
AsReturn (Select title, qty
From sales s, titles t
Where s.stor_id = @storeid and
t.title_id = s.title_id)
1.3.6 空列 和非 空列
SQL 支持空 列或 者空 表达式的概 念 当某 列为 空时 意味着 实际 值是 未知 的 第 2 章
将详细 讨论 如何 使用 空值 但是 当声 明一 个表 时 可以 为 某个允许 空 的列指定 Null 关键字
或者为 某个 不允 许空 的列指 定 Not Null 关键 字 当 SQL Server 带 有 这 种功能时 它把 没有
Null 或Not Null 的列 看作 是指 定 Not Null 的列 然而 这是 向后兼容 的 ANSI 标准的 SQL
语法 在这 种 SQL 语法中 把没 有 Not Null 的列 定 义看作 指定 了 Null 的列它对就 像 在 第
二章的 设置 最大 的 ANSI SQL-92 兼容性 节一样 执行 sp dboption 存储 过 程改变服务 器
的默认 值 或改 变指 定数据 库 的默 认值以符 合 ANSI 标准
sp_dboption AppDta, `ANSI null default'', true
前面例 1-8 中的 示例 和本书 其 他示 例 都为 那些不 允 许 空的列指 定 Not Null 符合 ANSI
标准 在本 示例 中 只有 Creditlimt 列 定 义为允 许空 对于 一个 允许 空的 列 当插 入或 者
修改一 行数 据时 可以 设 置该列 为空
1.3.7 用户 定义 的数 据类 型
SQL Server 提供了 一种 非标 准的数 据类 型 称 之为 用 户定义 的数 据类 型 而且 用户
定义的 数据 类型 是内 置数据 类 型 长度(如果合 适)和 可空性的 同义 词
为了创 建一 个用 户定 义的数 据 类型 使用 sp_addtype 存储过 程 如下 示例 所示
sp_addtype TDescription,`Character Varying (50)'', `Not Null''
本示例 创建 了 TDescription 数据 类型 作为 Character Varying(50) 数据 类型的同 义 词
该数据 类型 不允 许空 在 创建这 种数 据类 型之 后 可 以把它 用在 Create Table 列说明中 例

…,
ProductDescription TDescription
等价于
ProductDescription Character Varying (50) Not Null,..,
用户定 义的 数据 类 型 提供了 一 种很好的 方法 可以 标 准化用 于主 键和 外 键的数 据 类型
以及标准 化经常重复的列 的定义 在每个数据库内 数据类型名 称必须是 唯一的 可遵 循
一些约 定 每个 用户 定义的 数 据类 型以 T 开头 用于 区分 数据 类型 名称 和列 名 称
如果不 再需 要某 个用 户定义 的 数据 类型 那么 可以 使 用 sp_droptype 存储过程 删除 它
sp_droptype TDescription
1.3.8 缺省 值
在 Create Table 语句 上 可以 使用 Default 子句 为某列定义一个缺省 值 当使 用没 有 列
出基 表中所有 列的 Insert 语 句插入 一行数据 时 或者 通 过 一个 没 有 包括基表 中所有列 的视
图插入 一行 数 据 时 SQL Server 在 那些不在 列清单或者 视 图中的列 中放入一个 缺省 值 也
可以在 Insert 和 Update 语句 中使 用 Default 关键 字 设置 该列 的值 为缺 省值在本章 前面 的例 1-5 中 大多数 字符 列 的 缺省值 都是 空 格 然而 Name 列 没有缺 省值
并且不 允许 为空 所以 在 Customer 表中 插入一 行新数据 时 必须为 Name 列提供 一 个 值
CreditLimit 列的缺省值 为 Null 这个 特殊 的 Default 子句 实际 上没 有必 要 因为 SQL Server
假设 Null 是允 许空 的列 的 缺省值 而不 用明 确地 使用 Default 子句 EntryDateTime 列使用
Current_TimeStamp 函数作为 缺 省值 当插 入一 个新 行时 为 EntryDateTime 列指定 关键字
Default 可以 使 SQL Server 把当前系 统的 日期 和时 间放 在该 列中
对于 定义 该列 的数 据类 型 和长度 Default 子 句的 值必须是 有效 的( 如果该列 允许 空
那么 Null 关键 字也 是有 效 的) 就像 前面 提到 的 如 果该列 允许 为空 并且 没有指 定 Default
子句 SQL Server 就用 Null 就用 Null 作为 其缺 省值 如果 该列 不允 许为 空值 且没有 指定
Default 子句 那么 除非 为该 列明 确地 指定 一个 值 否则 不能 插入 一新 行 不能 为有 TimeSamp
数据类 型或 者 Identity 属性的列指 定 Default 子句
除 了 常量 值外 还可 以指定一 个常 量表 达式 这个 表达式 可 以包括 列在 表 1-3 中的任
何 niladic 函数 或者 列在 第 2 章表 1-2 中 的 其他标 量函数
注意 也可 以使 用 Create Default 语句 创建 一个 有 名称的 缺省 值 为 了把 这个有 名称
的缺 省值与某 个列 或者用 户定义 的数据类 型相 关联 可以使 用 sp_bindefault
系统存储过程 虽然有名称的缺省值提供 了一种重新使用缺省定义的 方式 但
是这种 方法 不是ANSI 标准 建议 有名 称的 缺省值仅用于用户定 义的 数据 类型
表1-3 允许用于 Create Table 的 Default 子句的 Niladic 函数
Niladic 函数 所使用的值
User 执行 SQL Insert 或者 Update 语句的用户的数据库用户名
Current_User Session_User
Current_Timestamp SQL 语句执行时的日期和时间( 返回与 GetDate 函数相同的值)
System-User 执行 SQL 语句的用户的登录帐号 ID
1.3.9 计算 的列
除了保存应用 程序数据的列 外 还可以在 表中定义计算的列 虽然计 算的列有一个列
名 但是 其定义的其余 部分只是使用 同一个表中的其 他列的 表达式 并且 可能是 常量或者
系统函 数 下 面的 示例 说 明如 何 通过连接 三个 其他 列 来创建 FullName 计算的列
Cerate Table AppDta.dbo.Employee
( EmplId Int Not Null
Check ( EmplId > 0 ),
LastName Char ( 30 )Not Null,
FirstName Char ( 30 )Not Null,
MdlInl Char ( 1 )Not Null,
...,
FullName As FirstName + MdlInl + LastName,
...
不能直 接插 入或 者修 改计 算 的 列 相反 当从 表中 检 索一行 数据 时 SQL Server 计算
2
这个值 在 计算 列 的 表达式中 不能 引用 其他 表中 的 列或者 使用 子查 询 子查询 在第 章
论述 也不 能在 主键 唯一 键或 者外 键或 者另 一个列 的 Default 子句 中 使用 计算的 列 SQL
视图 将 在本章后面的 创建视图 一节中讨论 提 供了一个能计 算列的超集 并且在 数
据 的 物理 存储( 基表) 和 数 据的逻 辑 表 示(视图) 之 间提供 了 一个有价值的区分 在大多数情 况
下 视图 是实 现计 算列 的 首选 方式
1.3. 10 增加 删除 和修改 表 列
可以使 用 Alter Table 语 句 在一个 已 有 的表中增 加一 个 新列 下面 的示 例说 明如 何 在本
章前面 例 1-5 中创 建的 Customer 表中增加 一个 新列
Alter Table AppDta.dbo.Customer
Add Discount Decimal ( 5,3 ) Default 0
Check ( Discount Between 0 And 100 )
Alter Table 语句为修改数据 库表提供 了灵活性 不 必 手 工删 除和重新 创建表 然而
注意所 有的 新列 要么 允许空 要么 有一 个 Default 子句 以便 SQL Server 可以为已 有 行初
始化该 列 否则 新 列的规 则等 同于 在 前面“创建表” 一节 中 Create Table 语句 的规则
Alter Table 还允许改变已 有 列的数据类型 大小和可 空 性 下面的语句设置 Discount
列的数 据类 型和 大小 及不允 许 空
Alter Table AppDta.dbo.Customer
Alter Column Discount Decimal ( 7,5 ) Not Null
一般不 能修 改 Text nText Image 或者 TimeStamp 列 不能 修改 计算 可复 制列 不能
修改计算 的列 约束 缺省值 或者索引中引用的列 这种规 则的例外是可 增加在 索 引中使
用的变 长度 列 的 长度 并且可 以改 变在唯一 性约 束或 者 检 查约 束 中使用的变长 度 列的长 度
另外 不能 修改 有 RowGuidCol 属性 的列 的类 型 大小 或者 可空 性 可以为 UniqueIdentifier
列增加 或者 删除 RowGuidCol 属性 这与 Alter Table 语 句 的 形式稍 微有些不 同
Alter Table AppDta.dbo.Customer
Alter Column RowId Add RowGuidCol
使用 Alter Table 语 句还 可 以从表 中删 除一 个已 有的 列
Alter Table AppDta.dbo.Customer
Drop Column Discount
在可以删除一 列之前 必须删除任何引用 该列的约束 缺省 表 达式 计算列表达式或
者索引 不能 删除 复制 的 列
注意 Alter Table 语 句 还 允许激活 和禁 止数 据库 触 发器 这在第 2 章讨 论
1.4 表 的 约 束
当创建 一个 表时 可以 有 选择地 指定 四种 类型 的约 束
主键
唯一性
外键
检查
注意 通过增加相应的子句作为 列定义的一部 分或者在最后的列定义之后 Transact-
SQL 允许指 定缺 省值 和约束 本书的 示例 涉及 到下 面 一些约 定
Default 子句 编码 为列 定义 的一 部分
涉及到 一个 列的 检查 约束编 码为 该 列定 义的 一部 分
其他约 束在 最后 一个 列的定 义 之后进 行 编码 按照 下 列顺序
主键约 束
唯一性 约束
外键约 束
检查约 束
1.4.1 非空 约束
非空约 束表 明本 列不 允许为 空
1.4.2 键约 束
主键 唯一性 和外键约束保护确认行或者 创建行之间关系的数据完整 性 当插入或者
修改表 中的 行 时 SQL Server 强制 这些 约束 并且在有外键 约束 的 情况 下 当 修 改或者 删
除被引 用表 中的 行时 SQL Server 强制外键 约束 最好 用 Constraint 关键 字再加上 一 个约
束名称 开始 每一 个约 束子句
Constraint CustomerPk Primary Key ( CustId )
Constraint 关键 字和 名称 是可 选的 但是 最好 使用 他 们 约束 是不 要求 的 尽管设计时
大多数 基表 都 有 一个 主键用作 表中 行的 唯 一 标识 符 例如 在例 1-8 中 CustId 列是用于
标识客 户的 主键 在 一个数 据库中 约束 名称 必须 是 唯一的
主键约 束的 基本 形式 如下:
Constraint constraint-name Primary Key Clustered NonClustered
(column-name ..)
On filegroup
主键最 多可 以有 16 个列 每一 个主 键列 的定 义不 允 许空 对 于一 个有 主键约束的表
SQL Server 禁止插 入 或 者修 改一行 以 免在 同一个表中 两 行的主键 列有 相同的值 一个 表
的定义 只能 有一 个主 键约束
唯一性约束类 似于主键约束 然而 列在 唯一键中的列可以为空 一 个唯一性键最多
可以有 16 个列 并 且一 个表 最多可有 250 个 唯 一性约 束 唯一 性约 束的 形式 也 类似 主键 约
束的形 式
Constraint contraint-name Unique Clustered NonClustered
( column-name,...)
On filegroup
对于每 一个 主键 和唯一性约 束 SQL Server 创 建一个内 部 索引 如 果为 任一约束指定
NonClustered 那么 SQL Server 为该约束创建一个非聚簇索引 如果为任何约束指定
Clustered 那么 SQL Server 为这个 约束 创建 一个聚簇索 引 为所 有其 他约 束创 建 非聚簇 索
引( 每 一 个 表 中 只 能有一个 聚 簇索引) 如果对任何 其 他 约 束 没 有 指定 Clustered 那么 SQL
Server 为没有 指定 NonClustered 的主键 约束 创建 一个 聚簇 索引 对于 唯一 性约 束 除非 指
定 Clustered 否则 SQL Server 创建一个 非聚簇索 引 使用 On 子句 可以把 主键 或者 外键
索引放 在一 个指 定的 文件组 上 这些 内容 在本 章后 面 其 他索引选项 一 节中讨 论外键是 一个 表(从属 表) 的一组 列 该表 的列 值匹 配另 一个 通 常与此 不同 的表 即父 表( 或
引用表)中 的主键值 或者唯一性键值 外键约束指定 外键的列( 在 同一个 表中 用 作 约束)和父
表中主 键或 者唯一性 键的列 SQL Server 要求父 表和从 属表在 同 一 个数据库 中 并且 每 一
个外键 列必 须与 相应 的主键 或 者唯 一键 列有相 同 的数据 类 型和 大小
参考一 下包 含有 关销 售信息 的 Sale 表 该表 包括 一个 ClustId 列 它包 含放 置订单的
客户 ID 可以 用下 面的 约束 创建 Sale 表
Create Table AppDta.dbo.Sale
( OrderId Int Not Null
Check ( OrderId > 0 ),
CustId Int Not Null,
...,
Constraint SalePk Primary Key ( OrderId ),
Constraint SaleCustFk Foreign Key ( CustId )
References dbo.Customer ( CustId ),
... )
SaleCustFk 外键约 束指 定 Sale 表中的 CustId 列是引用 Customer 表中 CustId 主键的外
键 使用 这种 约束 SQL Server 不允许应用 程序 在 Sale 表中 插入新行 除非该 行的 CustId
列包含 了 Customer 表中已有 的 CustId 值 在 7.0 以前的版 本中 这种 约 束也禁止 将 Sale 表
中一行 的 CustId 列修改 为在 Customer 表 的任 何行 中 都 不存在的 非空 值 换句话 说 新的 或
者修改 的 Sale 行必 须有 Customer 中的父行 外键 约 束也防 止父 表删 除或 者修 改 将会在 从
属表中 造成 的 孤行 (没 有匹配 的外 键值) 在 SQL Server 2000 中 用户有 了 更灵活 的选
择 在 SQL Server 2000 中 新增了 On Update,On Delete 两个子句 带有 NO
ACTION,CASCADE 两个参 数
ON DELETE 决 定了 当你 试图 删 除有外 键约 束的 行时 系统 所采 取的 措施
参数 NO ACTION 表明 删 除操作 失败 并返 回出 错信 息
参数 CASCADE 表明 带有 外键 约束 的所 有行 在指 向的 行被 删除 时 都 会被 随之删 除
ON UPDATE 及其 参数 的 用法同 ON DELETE
注意 如果 外键 中的 任何列 都 允许 空 那么 SQL Server 允许在属表中 的一行有 一个 外
键 该外 键有 一个 或者 多 个为空 的外 键列 总之 外键 列 不应允许为空
1.4.3 检查 约束
另一种约 束类型是检查 约束 它 指定在 一行成 功地插 入或者删除之前 必 须 满足的 条
件 列级的检 查约束测 试 单个列 的值 并 且编码 为列定义的一部 分 在本 章前面 的 例 1-8
中 CustId Name 和 CreditLimit 列有列级的 检查 约束 Name 列 的检查 约束 要 求一个 新 值
或者修 改值 不是 空格 也 不是空 值 如下
...
Name Char( 30 ) Not Null
Check ( Name <> ; ‘ ’)
...
每一个 列只 能有 一个 列级约 束 但是 允许 复合 条件 如在 CreditLimit 列中的约束
... CreditLimit Money Default Null
Check ( ( CreditLimit Is Null ) Or
( CreditLimit >= 0 )),
...
表级的检查约束在列定义之后 并且 就像 键约 束一 样 以 Constraint 关键字和约束名
称开始 然后 在 Check 关键 字之 后附 加用 括号 括住 的逻 辑表 达式
Constraint CustomerStatus Check
( ( Status <> ‘ ’ ) Or ( CreditLimit Is Null ) ) )
这种约束实现 下述策略 当 某客户被分配 一个信贷限额 甚至 可以是 0 时 他必须有
一个非 空格 状态 可以 有 多个表 级检 查约 束 它为 SQL Server 强制 数 据完整 性提 供了 一种
强大的 工具
注意 还可 以使 用 Create Rule 语句 创建 一种 规则 它类似于检 查 约 束 为 了把一个
规则与 一个 列或 者一 个用户 定 义的 数据 类型 相关 联 可以使 用 sp_bindrule 系
统存储过程 虽然这种规 则提供 了一种重复使 用规 则定义的方式 但是它不是
标准的ANSI 方法 另外 规则 只能 引用 一个 列 而表 级 约束 可以引用多个 列
如果使用用户 定义的数据 类型 那么可以考虑 在合 适时把 一个规则绑定到数据
类型上 为该 数据 类型 进 一步定 义有 效值 合并 使用 SQL Server 的 用户定义 数
据类型 和规 则 可以 获得更 接 近于 ANSI 标准 域的 功 能 而 这 种功能 在 SQL Server
中不能 使用
1.4.4 唯一 性约 束
最后一 种约 束是 唯一 性约束 具有 唯一 性约 束的 列在不 同 行的 取值 必须 为的 不同值 空
值例外 主键 约束 也可 用作 唯一 性约 束 但取 值不能 为 空
1.4.5 禁止 用于 复制 的约 束
可以为 外键 或者 检查 约束增 加 关键 字 Not For Replication 就像 下面 的代 码语 句 以便当
复制进 程修 改行 时 SQL Server 不强制这种 约束 这 种约束 仍可 以强 制用 户修 改
Constraint CustomerStatus Check Not For Replication
( ( Status <> ‘ ’ ) Or ( CreditLimit Is Null ) )
1.4.6 增加 删除 和禁 止约 束
在创建 一个 表之 后 需 要 增加或 者删 除约 束 Alter Table 语 句提供了 这种 功能
Alter Table AppDta.dbo.Sale
Drop Constraint SaleCustFk
Alter Table AppDta.dbo.Sale
Add Constraint SaleCustFk Foreign Key ( CustId )
References dbo.Customer ( CustId )
第一个 示 例 删除表中 外键 约 束 SaleCustId 第二个示例 增 加 一个 外 键 约束 使用 Alter
语句指 定约 束的 规则 与使用 Create Table 语 句的 规则一 样
如果不 想在已有 的数 据 上强 制 新的外键 或者 检 查 约束 那么可 以在 表 名 后面 增加 With
NoCheck 子句
Alter Table AppDta.dbo.Sale
With NoCheck
Add Constraint SaleCustFk Foreign Key ( CustId )
References dbo.Customer ( CustId )
还可以 临时 禁止 某个 约束 例如 插入 一行 不满 足约束的数 据
Alter Table AppDta.dbo.Sale
NoCheck Constraint SaleCustFk
当准备 再次 强制 约束 时 使用如 下的 语句
Alter Table AppDta.dbo.Sale
Check Constraint SaleCustFk
当禁止或者允许约束时 可以指定 All 关键字代替约束名称 在默认情况下 当允许一
个约束时 With NoCheck 选项发生作用 它在约束禁止保留在数据库中时允许插入或修改任
一行 为了检查数据库中的全部行是否仍然满足一个允许的约束 增加一个 With Check 子句
Alter Table AppDta.dbo.Sale
With Check
Check Constraint SaleCustFk
注意 当增 加或 者允 许约束 时 可以 临时 禁止 约束 或 使用 With Nocheck 选项 这两 个
约束环境 都允许有不满 足完整性规则 的数据 但 可能 导出这 种问 题 应 用 程序希 望所有 数
据满足 该表 的约 束
1.5 创 建 视 图
在 SQL 术语中 视 图是 一 种类表 对象 是某 人或 者应用 程 序执 行数 据库 查询时所显 示
的对象 然而 视图不 包含任何数据 相反 视图定 义在一 个或者多个基 表上 或者其 他
视图上 并且 提供 了一 种 访问 基 表中数据 的方 法 可 以使用 SQL 视 图做下 面的 工作
选择基 表中 行的 子集
只包括 一个 基表 列的 子集
基于一 个或 者多 个基 表列 导出 新的 视图 列
将 多 个基 表中 的相 关行 连 接 到视图中 的一 行 上
使用联 合运 算 合并 来自多 个 表中 的行集
视图提供 了一种简 化和限 制访问数据的方 法 例如 为了提 供一个只包含信 贷限额 至
少为$5,000 的客 户的 视图 可以 执行 下面 的语 句
Create View dbo.CustHighCredit As
Select
From AppDta.dbo.Customer
Where CreditLimit >= 5000
当执行 这个 Create View 语句 时 SQL Server 在 数 据 库的系 统表 中创 建该 视图 的 定义
SQL
一旦创 建了 该视 图 就可以像在其 他 语句中 使用表 一 样使 用该视图 例如 可以 执行
下面的 Update 语句来 修改所 选 行的 Status 列
Update CustHightCredit
Set Status = `B''
Where ShipCity = `Seattle''
And Status = `X''注意这 个 Update 在 该视 图 上 的工作 方式的 一些 重要 信 息 只有 那些 满足 全部 三个 条件
的行 CreditLimit >= 5000,ShipCity = `Seattle'' 和 Status = `X'' 才可以使其状态设置为 B 当
处理 Update 语句 时 SQL Server 只访问那 些满 足 CustHighCredit 视图 的选 择标准 的 数据 行
对于这 些行 SQL Server 接着应用 Update 语句的 选 择 标准来 确定 修改 哪些 行 正如 本例 所
示 可 以把 视图 看成 一个只 包 含那 些符 合指 定标 准的行的表
Create View 语句 是 SQL 中最复 杂的 DDL 语句 学 习 该语句 的最 好方 法是 一次 只看 一
部分 在 Create View 关键 字 的后面 应 提供视 图名 称 最 好使 用包 括视 图所 有者的 限定名称
在 Create View 语句 中 不能 使用 数据 库名 称限 定视图 名 称 因为 视图 总是 创建在 当前 的数
据库中 然而 在视图 定义中使用的 表名或者视图名 可以限 定 这样就允 许在其 他数据 库
的表和视 图上创建视图 在同一个数 据库中 对于 同 一个所 有者 视图名 称不能 与任何 的
基表名 称或 者视 图名 称相同
注意 如果 没有 限定 视图名 称 那么 SQL Server 使 用当前的用 户
Create View 语句 的下 一部 分是 视图 中 多达 1024 个列 名 的可选列 表 如 果 没 有指定 名
称列表 就像 前面 的示 例 那样 视图的结果表中的列名就 由在 As 子 句 后指定的 选择表 达
式定义 选择 表达 式与 在第 2 章中论述 的 Select 语句 的 结构类 似 选择 表达 式的 结 果 表确定
一个或者 多个基表或者 视图中 的哪 些列和行 包含在将 要定义 的视图 中 记住 结 果表是 一
个 SQL 概念 但不 必是 一 个存储 在磁 盘或 者内 存中 的 实际表 在下 面的 示例 中 选择 表达
式定义 了一 个结 果表 该 结果表 中有 Customer 基表中的 全 部列 和信贷限额至少是$5000 的
那些行
Select
From AppDta.dbo.Customer
Where CreditLimit >= 5000
注意 Select 子句 后面 的星 号()表示全 部列 当 创 建视图 时 SQL Server 在创建 视
图 时 在结 果表 中生 成列 清 单 如果以后 将这些列 增加到基表 中 那么 新列 就 不
能包含 在以 表示 列清 单 的视图中 直到 该视 图被 删 除和重 新创 建为 止
1.5. 1 定义 视图 内容
选择表 达式 可能 是掌 握 SQL 语法中 最重 要 也 常常 是 最高挑 战性 的方 面 选 择表达
式指定一 个由 From 子 句中列出的基表和视图中导 出的结果表 并且 用于视图定义 SQL
Select Update Delete Insert 和 SQL 游标声 明中 在有 关 SQL 的 DML(数 据操纵语 言) 的
下一章 中 我 们将 深入 研 究选择 表达 式和 SQL 相关部分 的 结构 现在 我们 只是 看几 个选
择表达 式的 简单 部分 以 了解 视 图 工作的基本 方式
1.5.2 选择 表达 式
选择表 达式 总是 以 Select 关键 字开 始 对于 最简 单的选 择 表达 式 在 Select 关键字 的
后面 有列 名清 单 或用暗示所有列的清单 以及 指 定一个 基表 或者 视图 的 From 子句
下面的 Create View 语句 使用 一个 选择表 达 式 该表 达式 用 Customer 表中 的全 部行 定义 一
个结果 表 但是 只包 含一部 分 列:
Create View dbo.CustShip As
Select CustId, ShipLine1,
ShipLine2,
ShipCity,
ShipState,
ShipPostalCodel,
ShipPostalCode2,
ShipCountry
From AppDta.dbo.Customer
引用 CustShip 视图 的 SQL 语句可 以把 它看 成一 个表 该表 由列 出的 列和 Customer 表
中的全 部行 组成
From 子句可以 列出 视图 和表
Create View dbo.CustHighCreditSeattle As
Select
From AppDta.dbo.CustHighCredit
Where ShipCity = `Seattle''
SQL Server 合并(AND)Where 子句 在一个视图上定义另一个视图 前面给出了
CustHighCreditSeattle 视图 的定 义只 包含 Customer 表中满 足 CreditLimit>=5000 且 ShipCity =
`Seattle'' 的行
1.5.3 合并 多个 表
From 子句 中也可 以列 出多个基表和视图 例如 下 面的语 句定 义一个连接 Customer
和 Sale 表中 相关 行的 视图
Create View dbo.Custsale As
Select Customer.CustId
Customer.Name,
Sale.OrderId,
Sale.SaleDate,
Sale.SaleTotal
From AppDta.dbo.Customer,
AppDta.dbo.Sale
Where Customer.CustId = Sale.CustId
注意 在From 子句 中 表和 视图最 多 可有 256 个
当在 From 子句中 指定 多个表 或 者视 图时 理论 上 SQL Server 生成 了一 个中 介结果
表 它包 含了列在表和 视图中的全部 行的全部组合 每一行 都 有 所 有表的 所有 列 从这 个
完整的 组合 集中 只有 那 些满足 在 Where 子句中 指定 条件 的行 才能 包括 在选 择 表达式 的结
果表中 而且 只有 列 在 Select 关 键 字 后面的列 才 包含 在结果 表中 在 这 个示例 中 只有
匹配 CustId 列值的 列值 才包 含在 结果 表中 最终 结果 表对 每一 个销 售( 有一 个匹 配的 客户)
包含一 行 而每一行 都 有销 售 数 据 和客户数 据 因为 在 Select 关 键 字的后 面 指 定 了明确 的
列清单 所以 结果 表中 只 有五个 列
注意 SQL Server 常常 使用 非常有效 的方法 来实 际实现引用 多个 表 的选 择表 达式 这
里 所 描述 的内 容是 多表 选 择表达式是 如何 工作 的逻 辑 定义
作为一 个连 接两 个表 的示例 见表 1-4(客户 样本 数据) 表 1-5(销 售样本 数据) 和表 1-6( 通过前面 的列 表中 定义 的 CustSale 视图 中看 到的 样本 数 据)
表1-4 Customer 表中的样本数据
Custld Name
10001 Ajax Plumbing
10003 Picante Software
10008 Bubba''s Grill
表1-5 Sale 表中的样本数据
Orderld SaleDate SaleTotal Custld
3678 1998-02-29 567.25 10003
3679 1998-03-06 1089.00 10001
3680 1998-03-06 376.50 10008
3681 1998-03-22 2012.90 10001
3682 1998-03-23 1233.00 10001
3683 1998-04-02 440.00 10003
表1-6 通过 CustSale 视图中看到的样本数据
Custld Name Orderld SaleDate SaleTotal
10001 Ajax Plumbing 3679 1998-03-06 1089.00
10001 Ajax Plumbing 3681 1998-03-22 2012.90
10001 Ajax Plumbing 3682 1998-03-23 1233.00
10003 Picante Software 3678 1998-02-29 567.25
10003 Picante Software 3683 1998-04-02 440.00
10008 Bubba''s Grill 3680 1998-03-06 376.50
注意 在本例 中 选择表达式中的列名是 如何使用这些列所在的表名 限定的 例如
Customer.CustId 指定在结果 表 中的 这个 列来 自 Customer 表的 CustId 列 为了 限 定一个 列名
在列名 前面 增加 一个 圆点(.) 后跟 表名 或者 是列 名
在这个 示例 中 没 有为 视 图的列 明确 指定 名称 所以 SQL Server 使用 结果表 中同 样的
未 限 定名 称 CustId Name OrderId SaleDate 和 SaleTotal 如果以 后打 算用 限定名引
用 CustSale 视图 的列 名称 那么应使 用 CustSale.CustId.CustSale Name 等等 如果在 选择
表达式 的结 果表 中有 重复的 未 限定列名 称 例如 Customer.CustId 和 Sale.CustId 都在结 果表
中 那 么 必 须为 视 图 的列 使 用 明确的和 唯 一 的列 名 这 些内容 在本章后 面 的“重 新 记录和 重
新命名 列” 一节 中描 述
选择表 达式 的 Where 子 句 是可选 的 如果 没有 指定 Where 子句 并在 From 子 句 上指定
一个表 或者 视图 那么 选 择表达 式包 括来 自基 表或 者 视图中 的所 有行 或者 如果在 From 子
句上指定多个 表和 视图 那么 选择表 达式 包括所有的行组合 Where 子 句可以指定一种 非
常复杂 的搜 索条 件 例 如 进行一 次测 试 在第 2 章 中对此深入 研究
1.6 视 图 分 类
1.6. 1 集群 视图
为适应 当今 大型 数据 库的要 求 SQL Server 2000 增加 了集 群视 图的 功能 由于 大型 Web站点和 数据 中心 的出 现 集群服 务器 这一 概念 得到 了 广泛的 应用 类似 于此 在 SQL Server
2000 中可 以把 一张原始 大数 据表分成 若干个 较 小 的成员 表 在每 一个 成员 表中 存储 着原 始
表的所有 行的一个子集 每张成员表 可以放在一个独 立的服 务器上 服务 器可 以 产生各 自
的视图 然后 用 Transact-SQL 语言的 UNION 语 句来合 并成总 视 图 就像 创建 自 一张完 整
的表一 样 如下 面的 例子
CREATE VIEW PartitionedView AS
SELECT
FROM MyDatabase.dbo.PartitionTable1
UNION ALL
SELECT
FROM Server2.MyDatabase.dbo.PartitionTable2
UNION ALL
SELECT
FROM Server3.MyDatabase.dbo.PartitionTable3
1.6.2 只读 视图 和可 修改 视图
选择表达式可以使用一个公共 值( 例如 在同一城市中的客户) 来 组合行 并且计算平
均值 例如 在一 个或者 多个列 上的总数 或者 平均值( 例如 一个 平 均折扣) GroupBy 和
Having 子句 指定 合计 这 些内容 在第 2 章中讨 论 目前 主要 应 了 解使用 Group By 和 Having
子句的 外部( 不是 嵌套) 选 择 表达式 的任 意视 图是 只读 视图 例 1-9 所示 的视图包含了 每 个 城
市的一行 在这个城市 中至少有一个 客户 对于该城 市中的 客户每一行有 城市名 称和平 均
的客户 折扣 率
例 1-9
Create View dbo.CustDiscountAvg
( ShipCity,
DiscountAvg )
As Select ShipCity,
Avg (Discount )
From AppDta.dbo.Customer
Group By ShipCity
这个视 图可 以包 含下 面这些 行
ShipCity DisclountAvg
Seattle 00.056
Eugene 00.009
Portland 00.012
Richmond 00.011
Loveland 00.003
Denver 00.024
注意 在这 个示 例中 视 图如何 没有 为 行指定 特殊 的 顺序 在 SQL 中 行序总是当检
索行 换言 之 在Select 语句 中 时指 定 而不 是 当定义 基表 或者 视图 时指 定
使用像这种的 视图 将不能通过视图插入 或者修改行 如果这是可能 的 例如 如果在 CustDiscountAvg 视图 上修 改 Eugene 行的 DiscountAvg 值 那么 SQL Server 可以改 变
Customer 基表的 哪些 行
如果第 一个 Select 跟着 As 关键字 不 包含至少 一个 直 接 没有 表达 式 从基表的列
中导出 的列 或者 第一 个 Select 指定 Distinct 关键 字 或者列 函数 例如 Max 那 么视图也
是只读 的 有子 查询( 嵌套的 Select 语句 在第 2 章中 讨论)的 视图也 是只 读的 不能使 用
只读视 图作 为 SQL Insert Update 或者 Delete 语句 的目 标
对于引 用视 图的 Delete Insert 和 Update 语句 还 有 附加的 限制 对于 多表 视图 不
能使用 Delete 语句 Insert 或者 Update 语句只 能 影响 一个基 表 如 果视图省 略 了没有缺省
值也不 允许 空的 基表 列 那么不 允许 执行 Insert 语句 Update 语 句不能 直接 修改 计算 列的
值 总之 改变 数据 库内容 的 最好方 法是 直 接在 基表上 执 行 Delete Insert 和 Update 操作
或者在基 于单个基表的 简单视图上执 行 如 果 使用 很 复杂的 视图 执 行修改 操作 一定要认
真测试 以便 得到 理想 的 结果
前面的 CustDiscountAvg 示例还 说明如 何明确编码视图列的名 称 在视图名 称后面 紧
跟列的 清单 如果 有 并用 括号 括住 每个 列 项 之 间用逗号分 开 视图的列名分别对应 于
选择表 达式 结果 表中 的列 在这 个 示例中 对应 关系如 下
视图的列名 结果表的列
ShipCity ShipCity
DicountAvg Avg(Discount)
不能为 视图 指定 列的 数据类 型 Null 或者 Not Null 缺省值 键 约 束或者 Identity 属性
视图列 的数 据类 型和 其它属 性由 基 于结 果表 列( 或 者表达 式) 确定 SQL 包括 Convert 函数
可以把 一个 列的 数据 类型或 者 长度 转变 到另 一个 导出的 列 中
1.7 创建视 图选项
1.7. 1 With Check 选项
Create View 语句 的另 一个 选项 是 With Check Option 该选项通 过选 择行 的子 集 的可修
CreditLimit<5000
改视 图限 制 行 的插 入 和 修 改操作 例如 下面 的视 图定义不 允 许 用 创建
行的插 入或 者修 改操 作
Create View dbo.CustHighCredit As
Select
From AppDta.dbo.Customer
Where CreditLimit >= 5000
With Check Option
这就防 止所 谓的 幻觉 修 改 在这种修 改 中 虽然可 以通过 视图插入 或 者 修改一 行
但是这 一行 不能 通过 该视图 检 索出 来 当视 图指 定 With Check Option 并 且另一 个视图 定义
在这个有 检查选项的视 图时 检查选 项限制 还应用到 从属的 视 图上 例如 为 了 在下面 的
视图中 插入 一行 该 行必须 有 CreditLimit 5000 而不 考虑 CustHighCreditSeattle 视图定 义
是否指 定 With Check Option:
Create View dbo.CustHighCreditSeattle As Select
From AppDta.dbo.CustHighCredit
Where ShipCity = `Seattle''
如果定 义在 另一 个视 图上的 视 图指 定 With Check Option 那 么所有低层 视图 的条 件 必
须满足 除了该条件外 还在要定义的视图上指定 而不考虑 低层 视图 的定 义是否指定
With Check Option
1.7.2 Schema Binding 选项
SQL Server 2000 的 CREATE VIEW 语句 还支 持 Schema Binding 选项 这一 选项 不允 许
视图的 引用 表进 行更 改 你可以 在创 建索 引的 任何 视 图上使 用 Schema Binding 选项
索引视 图 Indexed Views 也是 SQL Server 2000 的 新增特 性 它可 以大 大提 高 数据仓
库和决 策分 析系 统中 常用的 复 杂查 询的 性能
1.7.3 With Encryption 选项
正常情 况下 Create View 语 句的 完整文 本存 储在 syscomments 系统表中 可 以被有 访
问 syscomments 表读权限的任何用户检索 可以 为要 加密的 视图 在 Create View 语句和
syscomments 内容中 增加 一个 With Encryption 选项
Create View dbo.CustHighCredit
With Encryption As
Select
From AppDta.dbo.Customer
Where CreditLimit >= 5000
因为没 有办 法检 索加 密视图 的 源数 据 因此 如果 使用 With Encryption 选项 一 定要在
一个源 文件 中保 存原 先的数 据
1.8 创建视 图举例
既然我 们已 经研 究了 如何创 建 SQL 视图 的基 本知 识 现在多看 一 些 示例
1.8. 1 复合 条件
视图可 以使 用复 合条 件选 择数 据 行 示例 1-10 选 择 下述客户 不在 Richmond 信贷
限额在 1000 到 9999 之间 状态 是 A B 或 C
例 1-13
Create View dbo.CustSpecialCredit As
Select
From AppDta.dbo.Customer
Where ShipCity <> `Richmond''
And CreditLimit Between 1000 And 99999
And Status In ( `A'',`B'',`C'' )
2 SQL Between In
这个示 例使 用了 一些 在第 章中讨论 的 语法 特征 例如 和 它 可以提
高搜索 条件 的可 读性1.8.2 重新 排序 和重 新命 名列
可以重 新排 序和 重新 命名列 就像 下面 的示 例 1-11 它为运 输信 息提 供了 缩写 名 称
例 1-11
Create View dbo.CustShipAbv
( CsStrt,
CsCity,
CsSt,
CsZip,
CsCrRt,
CsAttn,
CsCnry,
CustId )
As Select ShipLine1,
ShipCity,
ShipState,
ShipPostalCode1,
ShipPostalCode2,
ShipLine2,
ShipCountry,
From AppDta.dbo.Customer
1.8.3 导出 列
下面的 示例 1-12 说明 如何使 用 Substring 和 concatenation(+) 操作在 SQL 视 图中导 出列
例 1-12
Create View dbo.EmpNamePhonePfx
( EmpId,
PfxVoice,
FullName )
As Select EmpId,
SubString ( PhoneVoice,5,3 ),
FirstName + MdlInl + LastName
From AppDta.dbo.Employee
SQL
在连接固定长度 的 字 符 列之 前 还提供了去掉尾部 空格 的方 法 下 面的表 达式 去
掉两端 的空 格 并 且在 名 称的 每 一部分之 间放 一个 空 格
LTrim( RTrim (FirstName ) ) + ‘ ’ +
LTrim( RTrim ( MdlInl ) ) + ‘ ’ +
LTrim( RTrim ( LastName ) ) + ‘ ’ +
注意 在这 个示 例中 LT r i m 和 RTrim 函数如何 嵌套来 去 除两 端的 空格
1.8.4 自连 接表
SQL 视图可 以自 连接 表 如 在下面的 示例 1-13 中 生成 一个 视图 其 中 每 一个雇员有
ID
一行 包括 该雇 员经 理的 和名称
例 1-13Create View dbo.EmpMgr
( EmpId,
LastName,
MgrEmpId,
MgrLastName )
As Select Emp.EmpId,
Emp LastName
Emp.MgrEmpId,
Mgr.LastName
From AppDta.dbo.Employee Emp,
AppDta.dbo.Employee Mgr
Where Emp.MgrEmpId = Mgr.EmpId
在这个 视图 的选 择表 达式中 From 子句列出了 同一个 表 Employee 两次 为了 清晰地
引 用 表的 相应 角色( 也就是 说 要么 是第 一个 角色 它是整个雇员集 要么 是第二种角色
它是检 索匹 配的 经理 行) 在 From 子句中引 用的 每一个 Employee 表后面有 一个相 关 名称( 也
称为别 名) Emp 用于 第一次引 用 Mgr 用于第 二 次引用 使用唯一的 相关 名 称作为列 的 限
定名代替模糊的表名 可以在任何选择表达式中使 用相关名 称 而不 仅是在指 定连接的 选
择表达 式中 使用 对于 嵌 套的或 者 其它 很长很 复杂 的 选择表 达式 简短 的相 关名称可使 SQL
代码更 易读
1.8.5 连接 四个 表
SQL 视图可 以创 建在 最多 256 个基表 上 包括 直接 列 在视图 的 From 子句上的表 以
及基于 From 子句中的 视图的 表 作 为最 后一个 示例 考虑 在例 1-5 中 定义的 Customer 表
和在例 1-14 中定 义的 三个表
根据本 章前 面的 例 1-5 中的 Customer 表 和在例 1-14 中 定义的 三个 表 例 1-15 中的
视图提 供了 一个 连接 说 明所有 客户 定购 的全 部零 件
例1-14
Create Table AppDta.dbo.Sale
(OrderId Int Not Null,
SaleDate Date Not Null,
SaleTotal Money Not Null,
CustId Int Not Null,
Constraint SalePk Primary key (OrderId)
Constraint SaleCustFk Foreign key (CustId)
Reference Customer (CustId))
Create Table AppDta.dbo.Part
(Part Id Int Not Null,
PartDesc VarChar(50) Not Null,
Constraint Part Pk Primary key (PartId))
Create Table AppDta.dbo.SaleItem
(OrderId Int Not Null,
PartId Int Not Null,Qty Int Not Null,
CustId Int Not Null,
Constraint SaleItemPk Primary key (OrderId,PartId)
Constraint SaleItemOrderFk Foreign key (CustId)
Reference Sale (OrderId),
Constraint SaleItemPartFk Foreign key (PartId)
Reference Part (PartId),
例 1-15
Create View dbo.CustSalePart
(CustId,
Name,
SaleDate,
PartDesc)
As Select Customer.CustId,
Customer.Name,
Sale.SaleDate,
Part.PartDesc
From AppDta.dbo.Customer,
AppDta.dbo.Sale,
AppDta.dbo.SaleItem,
AppDta.dbo.Part
Where Customer.CustId=Sale.CustId
And Sale.OrderId =SaleItem.OrderId
And SaleItem.PartId =Part.PartId
表 1-4(Customer) 表 1-5(Sale) 表 1-7(Part) 和表 1-8 SaleItem 显示了这 些基表 的 样
本数据 当使 用在 例 1-18 中定 义的 视图 检索 数据 时 结 果在表 1-9 中显示
表1-7 Part 的样本数据
Partld PartDesc
2654 Fax machine
3620 Stapler
4101 Desk
表1-8 SaleItem 表的样本数据
Orderld Partld Qty
3678 2654 1
3679 3620 10
3679 4101 2
3680 4101 3
3681 2654 2
3682 2654 1
3682 4101 1
3683 3620 5表1-9 通过 CustSalePart 视图看到的样本数据
Custld Name SaleDate PartDesc
10001 Ajax Plumbing 1998-03-06 Stapler
10001 Ajax Plumbing 1998-03-06 Desk
10001 Ajax Plumbing 1998-03-22 Fax machine
10001 Ajax Plumbing 1998-03-23 Fax machine
10001 Ajax Plumbing 1998-03-23 Desk
10003 Picante Software 1998-02-29 Fax machine
10003 Picante Software 1998-04-02 Stapler
10008 Bubba''s Grill 1998-03-06 Desk
可以看 出 SQL 有许多定义 视 图内 容的 方法 在第 2 章 进 一步研 究选择表 达式 时 我
们将学 习到 更多 的内 容 当使用 SQL 视图时 下面 的 提示有 助于 测试 它 们 使用查 询分 析
器输入 如下 语句
Select From CustSalePart
这个 Select 语 句显 示指 定 视图的 全部 列和 行 可以浏览 这 个结果 看 看 是否有自己希
望看到 的数 据
1.9 创 建 索 引
虽然在 Create Table 或者 Create View 语 句 中没有指 定一 个 特殊 的行 序 但是 SQL Server
确实使 用内 部索 引来 有效地 进 行行 选择 和排 序 当执行一条 DML 语句 例如 Select 语句
时 或者 当打 开一 个 SQL 游标时 SQL Server 自 动 选 择使用 的索 引 当指 定一 个主 键或 者
唯一性 约束 时 SQL Server 为基表创建 一个 内部 索引 可以 使用 SQL Create Index 语句创
建附加 的索 引
Create Index 语句 所要 求的 部分 相当 简单 写出一 个索引 名 称 然 后指 定 索 引要在其 上
创建的 一个 基表 和最 多 16 个列 索引 名称 不能 限定 因为 它 们 总是由表 的所 有 者拥 有 并
且创建 在与 表 相 同的 数据库中 在 一个表的 索引 集中 索引 名称 必须 唯一 为 了 避免混 淆
使用在 数据 库中 也是 唯一的 索 引名 称 即使 SQL Server 不要求这一 点 也应 如 此 不 能在索
引上使 用 Text nText Image Bit 或计算 机的 列 索引 中所 有列 总长度 是 900 个字节 下
面的示 例在 Customer 表的 ShipCity 和 CreditLimit 列上创建一 个索 引
Create Index CustCityCreditIx
On AppDta.dbo.Customer
( ShipCity,
CreditLimit )
与视图 不同 不 能直 接用任 何 SQL DML 语句 访问 SQL 索引 在 SQL 中 索引 只 在
SQL Server 内部使 用 他 们的 功 能 通常与 性能 相关
注意 可以 有选 择地 为索引 定 义指 定 Unique 选项 它与在 Create Table 语句 上指 定
唯 一 性约 束有 同样 的效 果 应该使用唯一性 关联起 来约 束而不是 单独 的唯 一性
索引 因为 它能 把表 定义和完 整 性 规则关 联 起来1.9. 1 聚族 索引
SQL Server 允许每 一个 基表 有一个 聚簇 索引 聚 簇索 引使表 中的 数据 与索 引一 同 存储
并使行的 物理顺序与索 引的顺序保持 一致 这种结构 可以大 大提高访问行 的性能 特别 是
对于有连续键值的行集 如果以 某种 特殊 的顺 序 频繁 访 问行( 例如 按名称访问雇员) 检
索相关 的行 集(例如 指定订单 ID 的订单 项) 或 者根据 值的 范围检索 行 那 么 考虑在 合适
的列上 使用 聚簇 索引 如 果这些 列已 经列 在主 键约 束 或者唯 一性 约束 中 可以 在 Create Table
的约束 子句 中增 加 Clustered 关键 字 这些内 容在 本章 前 面 键约 束 一节 已经 讨论 过 否则
可以在 Create Index 语句 中增 加 Clustered 关键字 如下 示例 所示
Create Clustered Index EmpNameIx
On AppDta.dbo.Employee
( LastName,
FirstName,
MdlInl )
如果没 有指 定 Clustered 关键 字 那么 SQL Server 创建一个 非聚 簇索 引
注意 对于一 个长键使用聚簇索引 要考 虑到 空 间 性 能的平衡 当表有一个聚族索
引时 SQL Server 存储一个聚簇键值作为 定位器 用于该表的非 聚族 索引
中的内容 对于一 个很 大的表 这样可能显著增加 非聚簇索 引所需 的空 间 对
于较小 的表 影 响不会太大 对于没 有聚 簇索 引的 表 SQL Server 使 用一个 内
部 的 行标 识符 作为 存储 每 一个索引项 的定 位器
1.9.2 其他 索引 选项
在 Create Index 语句 中 SQL Server 支持几 个其 他选项 这些 选项 汇总 在表 1-10 中
表1- 10 Create Index 语句选项
语句 选项
当在一个已经有数据的表上创建新索引时 x 指定要填充的每一个叶级
FillFactor = x
( 最低层) 索引节点( 页) 的百分比(0 到 100) 默认值是 0 它等价于 100
创建内部节点时总是有足够的空间分配 至少留有一个或者两个附加项
注意 不要将 FillFactor 用于没有任何数据的表 一般情况下 只有知道
将来表的改变方案时 FillFactor 才有意义
可以与 FillFactor 一同使用 以指定应在内部 以及叶级 索引节点上以
Pad_Index
相同的有效比率分配自由空间
指定 该表 已有 的聚 簇索 引应 该删 除 和重 建 然后 重建 所有 已存 在的 非 聚
Drop_Existing
簇索引 这样可以加快一些索引的创建
禁止自动重新计算过时的索引统计信息
Statistics_NoRecompute
注意 Create Index 语句 还支 持 Ignore_Dup_Key 选项 用于 向后 兼容 对 于新索 引
避免用 该选 项
下面的 示例 说明 如何 创建一 个 非唯 一的 聚簇 索引 其填 充因子是 25%:
Create Clustered Index EmpNameIx
On AppDta.dbo.Employee
( LastName,
MdlInl,
FirstName )
With Fillfactor = 25
为了增强性能 可以在一个与包含表中数 据不同的文件组上创建一个 非聚簇索引 为
了把索引放在一个指定的文件组上 在列 清单 和其 他 选项之 后( 如 果有其他 选项) 使用第
二个 On 子句
Create Index CustCityCreditIx
On AppDta.dbo.Customer
( ShipCity,
CreditLimit )
On AppDtaGroup2
索引对数据库 性能有正面影响和负 面影响 简单地说 索引可以加速 数据检索 但是
由于 SQL Server 需 要 花费 一些 时间 修改 基表 上 索引列 的 内 容可能减 慢修 改 索引 列的 值 作
为一种正 常的规则 在 行最初插入表 中后需要经常修 改的列 上创建索引时 一 定 要小心 这
种索引 应该 提供 显著 的检索 优 点 来平衡 它 们对修 改 的影响
1. 10 删除数据 库 表 视图 和索引
为了删 除数 据库 对象 可 以使用 下面 一条 语句
Drop Database database-name,...
Drop Table table-name,...
Drop View view-name,...
Drop Index table-name.index-name,...
使用这些语句 时一定要小心 当删除数据 库时 数据库中的全部对象 也都被删除了
当删除 一张 表时 引用 该 表的所 有索 引和 约束 也被 删 除 并且 当 删除 一 张 表或者 视图时
所有依赖 这张表或者视 图的视图都变 得无效 直到 用 同样的 名称 从 属视图 和引 用 的相应 列
创建另 一个 表或 者视 图为止
所有 Drop 语句 都允 许使 用以 逗号 分开 的对 象列 表 用于 Drop Table 语句 的 表名可 以
使用数 据库 名称 和所 有者名 称 限定 用于 Drop View 语 句的视图 名称 或者 用于 Drop Index
语句的 表名 可以 由所 有者名 称 限定 只能 在当 前数 据 库中删 除视 图和 索引
1.11 小 结
当创建数据库 对象时 一定要考虑命名 文档和该进程的其他方面 在开始时 稍微
费点劲 将更 易于 使用 和 管理数 据库 下 面列 出了 一 些有 助 于创建和 维护 对象 的 建议
在源文 件中 存储 创建 产品数 据 库 表 视图 和索 引的大 多 数 SQL 语句 把这 些语
句提取 到查 询分 析器 中或者 使 用 OSQL-i 命令执行 这些 语句
对于每 一个 数据 库 在 子 目录中 组织 SQL 源文件 例如\AppDta 对于每一 种 SQL
对象类 型 使 用包 含该 对 象名称 以及 表示 包括 哪些 语 句的 有 一定意义 的 源文件 名
例如
Create Database AppDta 创建数 据库 的 SQL 语句
Create Table Customer 创建表 的 SQL 语句
Create Table All 创建数 据库 中所 有表 的 SQL 语句
Create View CustHighCredit 创建视 图的 SQL 语句
Create View All 创建数 据库 中全 部视 图的 SQL 语句
Create Index CustCityCreditIx 创建索 引的 SQL 语句
Create Index All 创建数 据库 中全 部索 引的 SQL 语句
把注释 放在 SQL 源代码的开 始 描述 将要 创建 的对 象
在多行 语句 中对 齐列 名 数据类 型和 约束 以 方便阅 读
对所有 的 SQL 名称 创 建 和遵循 一个 良好 的命 名约 定 在数据库 表 视图 索
SQL (A-Z) (0-9)
引 列名 约束和其他 对象的名称中只使用字母 和数字 避免使
用 SQL 的保留字 作为 名称 例如 Order
使用用 户定 义的 数据 类型或 者 标准 的列 定 义 以提 供有 类 似功能的列的一致定 义
例如 对包 含简 短描 述文本 的 所有 列使 用 VarChar(50) 可以创 建 SQL Server 规则
和缺 省 值 并 且 把 它 们 绑定在 用 户 定 义的数据类型上 创建与由 ANSI 标准域提
供的类 似功 能
使用服 务器 连接 选项 和 sp_dboption 存储过 程来 设置 ANSI 标 准的各 种默 认行 为
在 Create Table 语句 上 为所 有列指 定 Not Null 选项 除了 那些 应该 接收 系统 空
值的列 一般 应对 唯一 性 和外键 字段 使用 Not Null
在 Create Table 语句 上 对于一 个没 有 为提 供一 个值的 Insert 操 作但 可 接收缺省 值
Default ( Create Table Default
的列增 加 子句 一 般 应 为某个列使 用 的 子句 而不 是
使用 Create Default 语句 和 sp_bindefault 系统 存储 过程)
在 Create Table 语句 上 对 任何应用到单个列 上 的 完整性 约 束编码列级的检查 约
束 一般 应使 用列 级检 查约 束 而不 是使 用 Create Rule 语句 和 sp_bindrule 系统
存储过 程
对大多数表 定义一个或者多个字段作为主键 约束 并且用 主键约束作为 确认 行
的主要 方式
在 Create Table 语 句 的 所 有 列定义之后 编码 任 何 键或者 多 列检查约束 使用 一
致的顺 序 例如 主键 之 后是唯 一性 键 再之 后是 外 键 最后 是检 查约 束
在约束 名称 中 使 用一 致 的前缀 或者 后缀 例如 Pk 表 示主键 Uk 表 示唯一性 键
Fk Ck
表示外键 表示 检查 约束
使用主 键或 者唯 一性 约束 而不 是使 用 Create Index 语句 来强 制唯 一的 键值
考虑潜 在的 修改 性能 和检索 性 能 决定 创建 哪些 索引
Transact-SQL 的数 据定 义语 言(DDL)是实 现 SQL Server 数 据库的 关键 是直 接输入 DDL
语句 还 是使用企业管 理器的图形界 面 还是通过 一个编程界面 执行 DDL 都有 助于 全 面
理解本 章研 究的这些 语句 一旦创 建了 合适的数 据库对 象 就可 以使 用在 第 2 章中研 究的
SQL 数据 操纵 语言(DML)第2 章 Transact-SQL DML 详解
本章主要介绍 4 个基本的 DML 语句 Select Insert Update 和 Delete 同时 将深入研
究 Transact-SQL 的 Select 达式 因为它是 Transact-SQL 查询和修改数据库的核心 本章还介
绍用于事务完整性的 Transact-SQL 工具 在本章的最后 将描述如何编写包含了多条 SQL 语
句的存储过程和触发器
2. 1 Select 语句
Transact-SQL 的 Select 语句 从一 个或 者多 个表 或者 视图 中检 索行 如果 使用 查询 分析
器输入 一条 Select 语句 那么结 果显 示在 结果 标签 窗 口中 如图 2-1 所示 可以 编辑 和打
印这些 结果 并且 把它 们 保存在 非数 据库 文件 中
2-1
图 查询分析器结果样本
为了使 用 Select 语句 列 出希望 在结 果中 的列 确认将 要访问 的表和视 图 并且指定
返回行 的选 择条 件 还 可 以 对行进行 组合 和排 序 Transact-SQL 将 Select 语句 上指 定的 信
息 和 系统 表中 的信 息合 并 起 来 以决定要 检索的 内容 和如何执行检索 Select 语 句的基本
结构如 下
Select select-list
From table-list
Where search-condition
Group By Grouping-column-list
Having search-condition
Order By order-by-column-list
有几个 附加 的子 句扩 展了 select 语句 的 Transact-SQL 版 这些 子句 我们 也会 看到
掌握 Select 语句 这种 非常强 大 并且 形式 非常 复杂的 功能 是成功使用 Transact-SQL 的关键 许多 Select 语句 的 形式类 似于 其他 的 Transact-SQL DML 语句形式 例如 Update
并且 就像 我们 在第 1 章 中 看 到的那样 选择 表达 式 是定义 Transact-SQL 视图的中 心 也
是完整 的 select 语句 的一 部分
下面的 示例 用于 说明 Select 语句 的每 一部 分 许多 示 例使用 图 2-2 到图 2-4 所示的三
个表
Create Table Customer
( CustId Int Not Null,
Name Char(30) Not Null,
ShipCity Char(30) Null,
DisCount Dec(5,3) Null,
Constraint CustomerPk Primary Key (CustId))
图2-2 Customer 基表样本
我们从 一个 最简 单的 select 语句 示例 开始
Select
From customer
Select 关键 字后 面的 星号() 表示全部 列 From 子句指定要使用的一个 或多 个基 表或 者
视图 在本 例中 是 Customer 表 在 From 子句中 可 以使用 数据 库名 称和 或所有者 名 称
来限定 表名 或者 视图 名 例如 AppDta.dbo.Customer 本 章中 的示 例使 用无 限定的 表名和视
图名称 来简 化 代 码 使之更容 易理 解 在这 个示 例中 因为 没有 进一 步限 制 要 检 索 的数据
也就是 说 没有 Where 子句 所以 检索 所有 行和 所有 列
Create Table Sale
(OrderId Int Not Null,
CustId Int Not Null,
TotalAmt Money Not Null,
SaleDate DateTime Not Null,
ShipDate DateTime Null,
Constraint SalePk Primary Key (OrderId),
Constraint SaleCustFk Foreign Key (CustId)
References Customer(CustId))图2-3 Sale 基表样本
Create Table Employee
(EmpId Int Not NUll,
FirstName Char(20) Null,
MdlInl Char(1) Null,
LastName Char(30) Not Null,
MgrEmpId Int Null
Constraint EmpPk Primary Key (EmpId)
Constraint EmpMgrFk Foreign Key (MgrEmpId)
References Employee (EmpId))
图2-4 Employee 基表样本
注意 “” 表示该语句 准备好时已有的全部 列 当交 互 式执行 一个 Select 语句 时 同时
准备和 执行 该语 句是 很必要 的 然而 在存 储过 程或者 HLL 程序中 语句 可能
在翻译阶段准 备( 作 为程 序 创建的 一部 分) 并在以后执行 如果在该语 句准 备
好以 后 在表 或视 图中 增 加了 一个 列 那么直到该 语句重新 准备 为止 这个新
列将不 包含 在暗 示的 列清单中
2.1.1 搜索 条件
前面 的示 例检 索全 部 客 户 数据 但是 假设只想 要 Seattle 城市的客 户 怎么办呢? 通过
增加一 个 Where 子句限 制检 索的 行 可以 完成这个 任 务
Select
From Customer
Where ShipCity=''Seattle''
最简单 的 Where 子句包 含了 一个 Transact-SQL 搜索条件 它是 一个 Transact-SQL 简单
条件 如上 所示 该搜 索 条件为 真的 全部 行都 被检 索 出来
当需要 使 用 Select 语句 检 索一个 指定 行时 可以使用主 键值指 定一个搜 索条件 如下
面的语 句
Select
From Customer
Where CustId=499320有主键 列的 Where 子句 允许 使用 Transact-SQL 进行单行 操作 对应于 HLL 内置的 I/O
操作(例如 Cobol Read 或者 Rewrite 语句) 可读取或者 修改一行 记录
Where 子句 还可 以包 含一 个搜 索条 件 该 条件 是由 两 个或者多 个 Transact-SQL 的简单
条件使 用 And 或者 Or 连 接 起来的 下面 的示 例说 明连 接( 使用 And) 两 个简单 条 件的搜 索条

Select
From Customer
Where ShipCity=''Seattle''
And Discount>0
为了对 条件 取反 可以 在 任何条 件的 开始 或者 在由 And 或者 Or 连 接的条件之前 指定
Not 逻辑 运算 符 为 了使用 Not 取反 一个 复合 条件 使用括 号括 住条 件
Not(ShipCity=''Seattle'' and Discount>0)
如果 ShipCity 不是 Seattle( 并且不 为空)或者如 果 Discount 0(并 且不为 空) 那么该条
件为真
还可以 使用 括号 指定 有 And 和 Or 的复 合条 件的 判断 顺序 如 下 面的示例
ShipCity=''Seattle'' Or Discount>0) And TotalAmt>100
SQL Server 首先判 断复 合条 件 ShipCity=`Seattle'' Or Discount>0 然后 这个 由 Or 连接
的条件 的结 果与 最后 一个条 件 TotalAmt>100 的值 取 And 如果没 有括 号 那么 Transact-SQL
首先判 断取 反表 达式 然后 是 And 连 接的条 件 最后 是 Or 连接 的 条件 因此 这个 示例
ShipCity=''Seattle'' Or Discount>0 And TotalAmt>100
等价于
ShipCity=''Seattle'' Or (Discount>0 And TotalAmt>100)
注意 这 个示 例不 等价 于 前面那 个用 括号 括住 由 Or 连接 的 条 件的示 例
2.1.2 三值 逻辑
Transact-SQL 条件是一个 逻辑表达式 对于 一个 给定 的行 可以是真 假或 者未 知
当 ANSI_Null 选项 打开 时 就像 在第 二章 的“ 设 置最大 的 ANSI SQL-92 兼容性”一 节 中推荐
的那 样 如 果 涉 及 到 比 较并且一个或者两个将比较的 值 为空 那么 该条 件的 结果未知 ( 回
忆一下 在第 1 章中 讨论 Creat Table 语句 时 允许 空的 列——使用 明 确 的或 者暗示 的 Null 属
性定义的列——可以 设置为 空 而不 是包含 一 个 有 效值 ) 例如 如果 ShipCity 是空的
条件 ShipCity= ''Seattle'' 的值未知 一般地 按照 ANSI 标准行为 对于任何比较
expression =expression
1 2
如果 expression 为空 或者 expression 为空 或者两 者都 为空 那么其结 果 未知 这种 规
1 2
则应用 到的 比较 运算 符是不 等 于 大于 或者 任何 其他可 能 性
注意 当 ANSI_Null 选项 关闭 时(不建议 如此) SQL Server 将空值作 为一 个实 际值
比较 null=null 判断 为真 而比较 null=non-null 判断为假
为了用 Not 取反 条件 或者用 And 和 Or 合并 条件 Transact-SQL 使用三 值逻 辑( 参见表
2-1) 而不是常 规的 二值 逻辑表2-1 Transact-SQL 的三值逻辑
p Q not p P and q p or q
真真假真真
真假假假真
真 未知 假 未知 真
假真真假真
假假真假假
假未知真假未知
未 知真未 知未 知真
未知 假 未知 假 未知
未知 未知 未知 未知 未知
这种 不 常 使 用 的 逻 辑 形 式对于处理其值可能是未知的 条件 是 很 必 要 的 正如 表 2-1 所
示 当用 And 连 接两 个条件 时(表中 的 p and q) 如 果 有一个 条件 的值 未知 那 么 这种连 接
也是未 知的
select 语句的结果 表只包含那些 Where 子句 搜 索条 件 为真的 行 如果搜 索条件为假或
者未知 那么 该行 就被 忽 略了 所以 如果 某行 有一个 空 的 ShipCity 列并且其 Discount 列
是 0.05 那么 这个 搜索 条 件
Where ShipCity=''Seattle'' And Discount>0
判断为 未知 并 且不 选择该行 注意 有一 个空 ShipCitp 列 的行也将 用 下 面的搜 索条
件忽略
Where Not (ShipCity=''Seattle'' And Discount>0)
2.1.3 从视 图中 检索 数据
Select 语句可以在 From 子句中使用视图以及基表检索数据( 回忆一下 视图是
Transact-SQL 的对象 定义 了一 种从 一个 或者 多个 基表 中访 问数 据的 方式) 当在 Select 语
句中指 定视 图时 SQL Server 合并视图 的定 义和 Select 语 句的定 义来 生成 结果 表 例如
考虑下 面由 Create View 语句 定义 的视 图
Create View CustSeattle
As Select
From Customer
Where ShipCity=''Seattle''
CustSeattle 视图 使用 Select 语 句 的一种形 式( 称 为选 择表达 式) 来指 定该 视图 只包含 Cus-
tomer 基表中满足 指定 条件 ShipCity=`Seattle'' 的行 可以 在一 个 DML Select 语 句 中使用该
视图 该 语句 进一 步限 制 返回的 行是 得到 折扣 的客 户
Select
From CustSeattle
Where Discount>0
该 Select 语句 的结 果表 只 包含 Customer 表中得 到 折扣的 Seattle 客户记录 一般当视图
Select Where SQL ( And
和 语句 都包 含 子句时 测 试 这 两个搜索 条件 的连 接 有效 地使用 运
算符)2.1.4 指定 要检 索的 列
一般情 况 下 希 望 包 含在结果 表中 的 列清 单 在 Select 关 键 字之后 就像 以前提到的那
样 当在 Select 关键字后面 指定 时 就表示全 部列 都 包含在结 果 表 中 不指 定 可以 列
出想要 的列 并用 逗号 分 开
Select CustId,
Name
From Customer
Where ShipCity=''Seattle''
2.1.5 剔除 重复 的行
当选择 的 列 表不 包 含 主键列时 结 果 表 可能会包 含重 复 的行 例如 下面的 Select 语
句为每 一个 客户 返回 一行
Select ShipCity
From Customer
但是 因为 结果 表中 只有 ShipCity 列 那 么可 能有 多 个行有 相同 的城 市值 为了从 结
果表中 剔除 重复 的行 可以 在 Select 关键 字之 后使 用 Distinct 关键 字 如下 面的语 句
Select Distinct ShipCity
From Customer
这 将 返回 至少 有一 个客 户 的 城市列表(没有 重复)
2.1.6 常量 函数 和表 达式
在 Select 关 键字 后 面 的选项 列 表 中 也可 以包含常 量 函 数和使 用列名 常量和函数 的
表达式
注意 Transact-SQL 还允许使用 IdentityCol 关键字 替代 有 Identity 属性的列的 列名 或
者使用 RowGuidCol 关键 字替代有 RowGuidCol 属 性 的列的 列名
例如 这个 Select 语句
Select Name,
''has a discount of'',
Discount 100,
From Customer
使用常 量和 算术 表达 式检索出如下所示的 结果 表
Smith Mfg. has a discount of 5.000 %
Bolt Co. has a discount of 2.000.%
Ajax Inc. has a discount of Null %
Adapto has a discount of .000 %
Bell Bldg. has a discount of 10.000 %
Floradel has a discount of .000 %
Alpine Inc. has a discount of 1.000 %
Telez Co. has a discount of .000 %
Nautilus has a discount of 5.000 %
Udapto Mfg. has a discount of .000 %
Seaworthy has a discount of 1.000 %
AA Products has a discount of 1.000 %Wood Bros. has a discount of 1.000 %
2.2 Select 的子句
2.2.1 Group By 子句
可以使 用 Group By 子 句 将 合计函 数应 用 到所选 行的 子组 中 例如 下 面的语句
Select Shipcity,
Count() As"Customer Count",
Avg(Discount) As"Avg.Discount"
From Customer
Group By ShipCity
为在不 同城 市中 的每 一组客 户 返回 一行 如下 所示
ShipCity Customer Count Avg.Discount
Albany 3 005000
Eugene 3 043333
Portland 4 027500
Seattle 3
03333
在这个 示例 中 ShipCity 是组合列 它把 Customer 表中的 行分 成组 一组 对应 每一 个
不同的 ShipCity 值 合计函 数 Count 和 Avg 应用到 每 一个组 并且 在结 果表中为每一 个 组
生成一 行 对于 任何 允许空 的 组合 列 认 为所 有的 空 值都在 同一 组中
注意 在这 个示 例中 有空 Discount 列的行 显然 不能 排除 允许 结果 集在每 一个 城 市
中有全 部客 户数 平均 折 扣值是对于 有非 空 Discount 列 的客户
在 Group By 子句中 可 以 列出列 名或 者标 量表 达式 下面 的 Group By 子句为每一 个
计算的 DaysToShip 和 SaleDate 列的组 合创 建一 个单 独的 组
Select SaleDate,
DateDiff(Day,SaleDate,ShipDate) As DaysToShip,
Avg(TotalAmt) As AvgAmt
From Sale
Where ShipDate Is Not Null
Group By SaleDate,
DateDiff (Day,SaleDate,ShipDate)
正常情 况下 在选 择列 表 和 Group By 子句中 列出 组合 的列 名( 和表 达式) 以便在最 终
的结果表 中 每一行有 该组的确认值 任何出现在选 择列表 中的其他列必 须用作 合计函 数
的参数
当使用 Where 子句 select 行时 可以 为组 合列 的某 个 特殊值 剔除 全部 行 当 某个组 没
有选择 的行 时 SQL Server 一般不为该 组在 结果 表中生 成 一行 因此 使用 图 2-2 中的数
据 如下 语句
Select ShipCity,
Count() As "Customer Count"
From Customer Where Discount>.01
Group By ShipCity
生成的 表中 没有 Albany 或者 Seattle 的行
ShipCity Customer Count
Eugene 2
Portland 2
为了使 每一 个组 合值 有一行 即 使是那 些组中 没有 行 的组 也可在 Group By 关键字之
All
后使用 关键 字
Select ShipCity,
Count() As "Customer Count"
From Customer
Where Discount>.01
Group By All ShipCity
这条语 句将 生成 下面 的结果
ShipCity Customer Count
Albany 0
Eugene 2
Portland 2
Seattle 0
2.2.2 Having 子句
在合计函数应用 到 组 合 行之后 可以使用 Having 子 句 限 制 结 果 表中的行 Having 子
句有一 个与 Where 子句 类似 的形 式 这 种 形式在 组 合行 之前选择行 例如 可 以输入 下面
的 Select 语句
Select Shipcity,
Count() As "Customer Count",
Avg (Discount) As "Avg.Discount"
From Customer
Where Discount Is Not Null
Group By ShipCity
Having Avg(Discount)>.01
为平均 折扣 率大 于百 分之一 的 城市检 索 出如 下信 息
ShipCity Customer Count Avg.Discount
Eugene 3 0.043333
Portland 4 0.027500
Having 子句的搜索条件可以包括组合列 例如 ShipCity 或者合计函数 例如
Avg(Discount)
注意 虽然 可以 在没 有 Group By 子句的 情况 下指 定 Having 子句 但是 很少 使用
Select
理论上 选 择表 达式 有一定 的顺 序 这部 分 语 句已论 述过 有助于 理解 何时 测
试 Where 和 Having 子句 的 搜索条 件 以及 其他子 句如 何起 作用 每一 步都从 前一步 的 中介结果表 中生 成一 个假 想结果 表 这些 步骤 如下
1) 列在 From 子句中的全 部 表 和视 图的 全部行的 全部组 合 都包 括 在 这一 步生 成的中介
结果表 中 ( 如果 在 From 子 句 中 只指 定一 个表 或者 视 图 那 么 中介 结果 表 的 列和行 与 在 指
定表或 者视 图中 的列 和行相 同
2) 如果 指定 一个 Where 子句 那么 这个 搜索 条件 应 用到由 第一 步生 成的 结果 表 中的每
一行 只 有 使 搜 索 条 件 为真的那些行才包括在由这一 步 生成 的 中 介 结 果表中 ( 如 果没有指
定 Where 子句 那么包 括第 一步 生成 的结 果表 中的 全部 行 )
3) 如果 指定Group By 子句 那么 来自 前面 一步 生成的 结 果表 中的 行组合成 单 独的 组
使一个 组中 的所 有行 对于所 有 组合 列都 有相 同的值 (如 果没有指 定 Group By 子句 那么
就认为 所有 行在 一个 组中 )
4) 如 果指定 Having 子句 那 么这种搜 索 条 件应 用 到 每一 个 组 只有使 搜索条件 为真
的行 的组才包 含在这一 步生成的 中 介 结果 表 中 ( 如果 没有 指 定 Having 子句 那么包 括上
一步生 成的 结果 表中 的所有 组 和行 )
如果指 定 Having 子句 但 是 没有指 定 Group By 子句 那么 这一 步生 成的 中介 结果 表
要么是 空的 要么 包含 前 几 步生成的 所有 行
5) 如果 既没 有指 定 Group By 子句 也没 有指 定 Having 子句 那 么这 一步生 成的 中介
结果表包 括第一步和第 二步生成的结 果表中的行 每 一行包 含在 选 择列表 中 指定 的直接 的
和导出 的列
如果指 定了 Group By 子句或者 Having 子句 或者 这两 个子 句 那么 这一 步生 成的 中介
结果 表 为 在 第 一 步 到 第 四步生成的每一个行组包括一 行 ( 如果前 面的结果表是空的 那么
这 一 步生 成的 结果 表也 是 空 的 ) 每一行包含包括在选 择 列表中 的 任何组合列 以及 将选择
列表中 的合 计函 数应 用到组 的 结果
6) 如果 对选 项列 表指 定 Distinct 关键字 那 么 删 除前面 步 骤中 生成 的结 果表中的冗 余
行 否则 所 有的 行都 包 括在最 终的 结果 表中
这一系 列步 骤为 理解 选择表 达 式的 结果 提供 了一 种方式 不 必理解 SQL Server 实际上
是如何 执行 Select 或者 其 他语句 的 SQL Server 的优化器 可以 使用 索引 或者 其他技 术 来生
成等价 的结 果
2.2.3 Where 子句 和Having 子句
因为根 据组 合列 的值 来选择行时 既可以 使用 Where 子句 也可 以使 用 Having 子句
所以可 以选 择下 面一 条语句 检索 在 Portland 或者 Seattle 中 客户的 平均 折扣 率
Select ShipCity,
Avg(Discount)
From Customer
Where Shipcity In (''Portland'', ''Seattle'')
Group By ShipCity

Select ShipCity,
Avg(Discount)
From Customer Group By ShipCity
Having ShipCity In(''Portland'', ''Seattle'')
使用 Where 子句 是一种 编码 这种 检索 语句 的较清 晰 方式 在有 些情 况下 把 测 试放在
Where 子句 中执 行明 显快 于把 这种 测试 条件 放在 Having 子句 中执 行 因为 SQL Server 在
组合步 骤和 计算 合计 函数值 之 前剔 除了行
2.2.4 Order By 子句
使用 Order By 子句 可以 在 Select 语句 的 结果 表 显示 或者返 回到 程序 之前 排列 顺序
Order By 子句使用 升序 或者 降序(使用 关键 字 Desc) 指定 一组 列 对于 某个 在结 果表 中无 名
称的 列(例如 由表 达式或 者函数 指定 并且 没有别名) 可以使 用一 个 相 对列 号 来 代替列 名
指定使 用无 名称 的列 对行排 列 顺序
注意 虽然 Order By 子句不 是选 择表 达式 的一 部分 但 是它只有 在 Select 语 句 中才有
效 因此 当执 行一 条交互 式 的 Select 语句 或者 在一个 Insert 语 句中时 或者
在程序 中声 明游 标时 可以使 用 Order By 子句 但是 不能 在视 图定 义中 使 用 Order
By 子句
下面这 条语 句按 照客 户 ID 的顺 序检 索已 经运 输的 订 单 并且在客户 ID 内 根 据运输
订单耗 费的 天数( 最 长的 时 间间隔 排在 第一 位)排序
Select CustId,
OrderId,
SaleDate,
ShipDate,
DateDiff(Day,SaleDate,ShipDate)
From Sale
Where ShipDate Is Not Null
Order By CustId,
5 Desc
在 这 种情 况下 从 DateDiff(Day,SaleDate,ShipDate) 表达式中 产生 的无名称的第五 列被
用来对 CustId 值相同的 行排 序
对于使这条语句可读性更高 可 在 表达式 或者 函数 后 面加上 As 关 键字和一个 列 名
为一个 导出 的列 编码 一个别 名 用这 种技 术重 写前 面 的示例
Select CustId,
OrderId,
SaleDate,
ShipDate,
DateDiff(Day,SaleDate,ShipDate) As DaysToShip
From Sale
Where ShipDate Is Not Null
Order By CustId,
DaysToShip Desc2.3 复杂的 Select 语句
上面示 例 说 明了 Select 语 句的基 本组 成部 分 在下一个 示例中 我们将 看一些比较复
杂的变 化 从选 择列 表中开 始 我们 还将 研究 一些 在 Transact-SQL 中可以使用的 附 加子 句
2.3.1 在From 子句 中指定 多 个表
From 子句中最 多可 以列 出 256 个表 和视 图 如果 在 From 子句中列出 了多 个表 或者 视
图 那么 执行 Select 语 句时就 好 像 指 定了一 个 表 该表有 指定 表 中 的全部列 和这 些 表中 所
有行的 可能 组合 即两 个 表的笛 卡尔 乘积 虽然 当 执行多 表 Select 语句 时 这样 大的 中介
表并不 总是 创建 但是 这 是考虑 所发 生事 件的 最简 单 的方式 例如 在图 2-2 和图 2-3 中
的两个 表可 以用 在下 面的 Select 语句 中
Select
From Customer,
Sale
结果列 见表 2-4 它并 没有什 么 用 因为 有些 行的 组合 信息 来自 不相 关的 客户 和销 售
表2-4 使用笛卡尔积生成的检索样本
客户表中的列 销售表中的列
Custld Name ShipCity Discount Orderld Custld TotalAmt SaleDate ShipDate
133568 Smith Mfg. Portland .050 234112 499320 35.00 1998-05-01 1998-05-15
133568 Smith Mfg. Portland .050 234113 888402 278.75 1998-05-01 1998-05-04
133568 Smith Mfg. Portland .050 234114 499320 78.90 1998-05-03 Null
133568 Smith Mfg. Portland .050 234115 890003 1,000.00 1998-05-04 1998-05-10
133568 Smith Mfg. Portland .050 234116 246900 678.00 1998-05-04 1998-05-08
133568 Smith Mfg. Portland .050 234117 133568 550.00 1998-05-05 1998-05-08
133568 Smith Mfg. Portland .050 234118 905011 89.50 1998-05-05 1998-05-10
133568 Smith Mfg. Portland .050 234119 499320 201.00 1998-05-05 Null
133568 Smith Mfg. Portland .050 234120 246900 399.70 1998-05-06 1998-05-08
246900 Bolt Co. Eugene .020 234112 499320 35.00 1998-05-01 1998-05-15
246900 Bolt Co. Eugene .020 234113 888402 278.75 1998-05-01 1998-05-04
246900 Bolt Co. Eugene .020 234114 499320 78.90 1998-05-03 Null
246900 Bolt Co. Eugene .020 234115 890003 1,000.00 1998-05-04 1998-05-10
246900 Bolt Co. Eugene .020 234116 246900 678.00 1998-05-04 1998-05-08
246900 Bolt Co. Eugene .020 234117 133568 550.00 1998-05-05 1998-05-08
246900 Bolt Co. Eugene .020 234118 905011 89.50 1998-05-05 1998-05-10
246900 Bolt Co. Eugene .020 234119 499320 201.00 1998-05-05 Null
246900 Bolt Co. Eugene .020 234120 246900 399.00 1998-05-06 1998-05-08
275978 Ajax Inc. Albany Null 234112 499320 35.00 1998-05-01 1998-05-15
275978 Ajax Inc. Albany Null 234113 888402 278.75 1998-05-01 1998-05-04 续表
客户表中的列 销售表中的列
Custld Name ShipCity Discount Orderld Custld TotalAmt SaleDate ShipDate
275978 Ajax Inc. Albany Null 234114 499320 78.90 1998-05-03 Null
275978 Ajax Inc. Albany Null 234115 890003 1,000.00 1998-05-04 1998-05-10
275978 Ajax Inc. Albany Null 234116 246900 678.00 1998-05-04 1998-05-08
275978 Ajax Inc. Albany Null 234117 133568 550.00 1998-05-05 1998-05-08
275978 Ajax Inc. Albany Null 234118 905011 89.50 1998-05-05 1998-05-10
275978 Ajax Inc. Albany Null 234119 499320 201.00 1998-05-05 Null
275978 Ajax Inc. Albany Null 234120 246900 399.70 1998-05-06 1998-05-08
499320 Adapto Portland .000 234112 499320 35.00 1998-05-01 1998-05-15
499320 Adapto Portland .000 234113 888402 278.75 1998-05-01 1998-05-04
......
890003 AA Portland .010 234119 499320 201.00 1998-05-05 Null
Products
890003 AA Portland .010 234120 246900 399.70 1998-05-06 1998-05-08
Products
905011 Wood Bros. Eugene .010 234112 499320 35.00 1998-05-01 1998-05-15
905011 Wood Bros. Eugene .010 234113 888402 278.75 1998-05-01 1998-05-04
905011 Wood Bros. Eugene .010 234114 499320 78.90 1998-05-03 Null
905011 Wood Bros. Eugene .010 234115 890003 1,000.00 1998-05-04 1998-05-10
905011 Wood Bros. Eugene .010 234116 246900 678.00 1998-05-04 1998-05-08
905011 Wood Bros. Eugene .010 234117 133568 550.00 1998-05-05 1998-05-08
905011 Wood Bros. Eugene .010 234118 905011 89.50 1998-05-05 1998-05-10
905011 Wood Bros. Eugene .010 234119 499320 201.00 1998-05-05 Null
905011 Wood Bros. Eugene .010 234120 246900 399.70 1998-05-06 1998-05-08
但是如 果使 用选 择列 表并且 增 加一 个 Where 子句 那么 可以 得到 一个 很有 用的表
Select Sale.OrderId,
Sale.CustId,
Customer.Name
From Customer,
Sale
Where Sale.CustId=Customer.CustId
下面所 示的 结果 表提 供了一 个 清单 包括 客户 的 ID 和 每一个销 售人 员姓 名
Orderld Custld Name
234112 499320 Adapto
234113 888402 Seaworthy
234114 499320 Adapto
234115 890003 AA Products
234116 246900 Bolt Co. 续表
Orderld Custld Name
234117 133568 Smith Mfg.
234118 905011 Wood Bros.
234119 499320 Adapto
234120 246900 Bolt Co.
这种两表 运算称作 相等连 接 这是一种最常用和最 有用的关 系型数据库运算 相等 连
接只选择 笛卡尔积中的 行 其中 同表中的相关列有 相等的 值 在这个示 例中 相等连 接
选择的 列中 销 售表 中的客 户 ID(Sale.CustId)匹配客户表中的客 户 ID(Customer.CustId) 除
了 这 种相 等连 接 Transact-SQL 允许将要 连接 的表 使用 其他 比较 运算 符 例如 大于(>) 或者
不等于(<>) 正如 本例 所 示 当多个 表中 有同 名的 列 时 可以使 用 table.column 形 式 限制列
名以避 免混 淆不 清
注意 在下 一示 例中 要说明 限定 名称 可以 是一 个关联 名 称而 不是 一个 表名
还可 以使 表自 己连 接 也 就是说 一个表可以在 From 子句中假设有好几个角色 当
在任何 其他 子句 中指 定列名 时 为了 保证 清除 所指 那 一个表 的角 色 必 须为任何列 在 From
子句后 的表 增加 一个 唯一的 关 联名 称 也称 为表的 别名 例如 输入 下 面 的语句
Select Emp.EmpId,
Emp.LastName,
Mgr.LastName
From Employee As Emp,
Employee As Mgr
Where Emp.MgrEmpId=Mgr.EmpId
生成如 下所 示的 表 该表包 括 了雇 员姓 名和 他们 的经理姓名
Emp.Empld Emp.LastName Mgr.LastName
104681 Rillens Allen
227504 Waterman Herbert
898613 Allen Herbert
899001 Castor Allen
在这个 示例 中 Employee 表用于 两种 角色 一种 角色 提供 雇员 组(关 联名称 是 Emp)
另一 种角 色提 供了 查询 表 用于 寻找 与每 一个 MgrEmpId 值相对应的名称( 关联名称是
Mgr) 关联 名称 在表 名和 可选 的 As 关键字 之后 使用 一个 限定 的列 名如 Emp.LastName
可使列 值所 在的 Employee 表的角 色更 加清 楚
对于在 两个 表之 间的 简单内 连 接(不匹 配的 行从 结果 中 删除) 使用 From 和 Where 子句
的技术就很合适 对于 比 较复杂 的外 连接( 不匹 配的 行 仍然保 留在 结果 中) 或者 对于多 个
表用在 有一 个或 者多 个连接 的 查询 语句 中的 情况 可以 在 From 子句中明确 地使用 join 语
法 前面 的示 例也 可以 写 成如下 的形 式
Select Emp.EmpId,
Emp.LastName,
Mgr.LastName
From Employee As Emp Join
Employee As Mgr
On Emp.MgrEmpId=Mgr.EmpId
SQL Server 还有变 化的 join 语法 用于 支持 在左 外连 接 右 外连接 和全 外连 接的变化
左外连 接的 语法 如下
Select Customer.CustId,
Customer.Name,
Sale.SaleDate
From Customer
Left Outer Join
Sale
On Customer.CustId=Sale.CustId
该 Select 语句 的结 果如 表 2-5 所示
表2-5 使用左外连接的检索样本
CustId Name SaleDate
133568 Smith Mfg. 1998-05-05
246900 Bolt Co. 1998-05-04
246900 Bolt Co. 1998-05-06
275978 Ajax Inc. Null
499320 Adapto 1998-05-01
499320 Adapto 1998-05-03
499320 Adapto 1998-05-05
499921 Bell Bldg. Null
518980 Floradel Null
663456 Alpine Inc. Null
681065 Telez Co. Null
687309 Nautilus Null
781010 Udapto Mfg. Null
888402 Seaworthy 1998-05-01
890003 AA Products 1998-05-04
905011 Wood Bros. 1998-05-05
右外连 接的 语法 使用 Right Outer Join 关键字 并且 扩展到运算符右端 表中为空的不匹
配行 如果 使用 Full Outer Join 关键 字 那么 两个 表 中的不 匹配 行都 包括 在结 果 表中 如左
下连接 和右 外连 接所 述
注意 SQL Server 还有非标 准的连 接运 算符=( 左外 连接) 和=( 右外 连接) 用于兼容 以
ANSI join ( )
前的版 本 这些 运算 符不等 价 于 标准的 关键 字 如 上所述 并且不 应
该用于 新的 查询 语句
2.3.2 Union 关键 字
通过指定 两个集的 联合 也可以导出左外 连接 不匹配 因此连接起来 的 行集和 第
一个表(左表)不匹配的行用空值 或任 何其 他的 值 扩 展用于 第二 个表 中的 列 例如 可
Select
以输入 下面 的 语句 检索 包含 所有 客户 和他 们 的销售 日期 如果有 的 左 外连接
Select Customer.CustId,
Customer.Name, Sale.SaleDate
From Customer,
Sale
Where Customer.CustId=Sale.CustId
Union
Select Customer.CustId,
Customer.Name,
Null As SaleDate
From Customer
Where Not Exists
(select
From Sale
Where Sale.CustId=Customer.CustId)
这个示 例使 用 SQL Union 运算符 它将两 个 Select 表达 式 的结 果表合并到一 个 的结 果
表中 第一 个 Select 语 句 检索至 少有 一个 匹配 Sale 行的 Customer 行( 内连 接) 第二 个 Select
语句使 用一 个嵌 套的 Select 语句( 在“Exists 条件” 一 节 中解释)只 返回匹配的 行 重要的 是
第二个 行集 为每 一个 Customer 行包 括一 行 其中 不 存在任 何匹 配 Sale 表中 的行 完整
的 Select 语句(联合 两个 选 择 表达式) 的 结 果与表 2-5 所 示的左外 连接 相同
包含 Union 运算 符的 Select 语 句可 以指 定一 个结 果表作 为 由选择表达式定义的 两 个或
者多个 中介 结果 表的 联合 回忆 一下 选择 表达式 是 Transact-SQL 表达式的一种形式 它
以 Select 关键 字开 始 有一 个 From 子句 并且 可以有 Where Group By 或者 Having 子句
选择表 达式 本身 没有 Union 运算 符或 者 Order By 子句 完整 的 select 语句 可以 是简
单的一 个选 择表 达式 或者是 两 个或 者多 个选 择表 达式的 联 合 可以 有 Order By 子句 当 Select
语句包 含一 个 Union 运算符 时 在最 后一 个 选择 表达 式之后 指定 Order By 子句 并且 决定
在选择 表达 式中 行联 接的顺 序
为了指定两个 选择表达式的联合 这些选择表达式的结果表必须与联 合兼容 其含义
是它们必须有相同数量的列和每一双对应的列( 根据在各自的选择列表中的位置相对应)
必须有兼容的列定义( 或 者 是相同 的数 据类 型 或者允许隐含地转换) 结果表 中 的列名 是
在第一 个选 择表 达式 中使用 的 列名 或者 别名
当指定 Union 运算 符时 冗 余 行一 般从最 终的结 果表中 剔 除了 如 果在 结果 表中的所
有列都 有相 同的 值 那 么 两个行 是冗 余的 可以指 定 Union All 在结 果表 中包括 冗 余行
下面的 示例 说明 如何 用 Union All 得到 全部 雇员 和联 系人 的姓
Select LastName
From Employee
Union All
Select LastName
From Contractor
可以使用的另 一种技术是对结果行中的每 一行增加一个标记列 以显 示 该行来自哪一
个选择表达式 下面的 Select 语句检索全部雇员和联系人的姓名( 包 括姓名 相同 的人) 并
且根据 姓名 排序
Select ''Employee'' As Tag,
FirstName, MdlInl,
LastName
From Employee
Union All
Select ''Contractor'' As Tag,
Firstname,
MdlIn1,
LastName
From Contractor
Order By LastName,
FirstName,
MdlInl
结果表 的样 本如 下所 示 每一行 包含 一个 标记 表 示 该人是 雇员 还是 联系 人
Tag FirstName MdlInl LastName
Employee Trish S Allen
Employee Rich D Castor
Contractor Bill M Dutcher
Employee Dave R Herbert
Contractor Cricket S Katz
Contractor Tim L Murphy
Employee Barb L Rillens
Contractor Richard M Russle
Employee Greg J Waterman
该示例 还说 明如 何编 码 Order By 子句来对 联合两 个选择表达 式 的 Select 语句 中的 行排

前面的 那些 示例 说明 了 Select 语句 的大 部分 内容 在 下一节 我们 将深 入研究 在 Where
和 Having 子句 中使 用的 搜索 条件 搜索 条件 是 选择 表 达式的 重要 组成 部分 它是 Transact-
SQL 的“ 设置 在某 点” 功 能 的核心 必须 很好 地理解 才能有效地使用视图 Select Insert Update
和 Delete 语句 以及 Transact-SQL 游标
2.4 Select 的条件
正像我 们所 看到 的那 样 选择 表达式 的 Where 和 Having 子 句有一个搜 索 条件 包含
了一个 简单 的条 件 或者由 And 或者 Or 连接的 多个 简单 条件 回忆 一下 一个 简单 的条
件指定 一个 逻辑 表达 式 判断为 真 假或 者未 知 Transact-SQL 有好几种 条件 包括 Basic
Null Between In Like Exists 和 Quantified
注意 Text NText 和 Image 数据类型 只能 用在 Like 条件 中和 允许使用 Text 和 Image
参数的 函数 例如 PatIndex 中 Text 和 Image 数据类型不 能 用在 子查 询的选择列
表中2.4.1 Basic 条件
Basic 条件 使用 下面 列出 的 比较运 算符 来比 较两 个值
运算符 含义
= 等于
<> 不等于
< 小于
<= 小于或者等于
> 大于
>= 大于或者等于
Transact-SQL
还允许下 面的等 价 运算 符
运算符 等价于
!= <>
!< >=
!> <=
我们已 经看 到了 几个 简单的 Basic 条件 示例 其一 般的 语法 形式 是
expression expression
1 2
其中 每 一个表达式可 以是列名 文字 或者 一些 有 效的 运 算 字 符串或者其它形 式
的表达 式 是上 面列 表 中的一 个逻 辑比 较运 算符 这种条 件的 一个 示例 如下
Customer.CustId Sale.CustId
Basic 条件 还可 以采 用下 面 的形式:
Expression (select-expression)
其中 expression 和 与上面有同样的含义 select-expression 指定一列并且 生成只有
一行的 结果 表 下面 的 select 语 句显示 比平 均 折扣 率高 的所有客户
Select CustId,
Name
From Customer
Where Discount>(select Avg (Discount)
From Customer)
在搜索 条件 中使 用的 选择表 达 式称 作子 查询 在上 面 的示例 中 子查 询是
Select Avg(Discount)
From Customer
这个特殊的子 查询生成一个标量值—— 也就 是说 只有一行和一 列的结 果表 在本 例
中 该 值 是 全部客户 的 平均 折 扣 率 然后 外部 Select 语 句 的 搜索条件 比 较 每一 个 客 户 的
折扣率 和平 均的 折扣 率 并且在 Select 语句 的结 果表中 只包括那些 Discount 列 值 大于平 均
值的 Customer 行
2.4.2 Null 条件
回忆 一 下 利用 ANSI 标 准行为 比较 两个 值 时 如 果其中一个值 或 者 两 个 值 都 是空
的 那么 该比 较值 未知 Null 条 件提 供了 一种 测试 空或 者非 空的方式 语 法如下Where ShipDate Is Null
或者
Where ShipDate Is Not Null
2.4.3 Between 条件
Transact-SQL 还有一些 复合 条件 的快 捷形 式 Between 条 件是两个 不等 值测 试的 方法
搜索条 件
Where Discount Between 0.01 And 0.02
等价于
Where Discount>=0.01
And Discount<=0.02
还可以 使用
Where Discount Not Between 0.01 And 0.02
等价于
Where discount <0.01
Or Discount >0.02
2.4.4 In 条件
为了简 化一 系列 相等 性测试 可以 使用 In 条件 例 如 搜索条 件
Where ShipCity In(''Eugene'', ''Portland'', ''Seattle'')
等价于
Where ShipCity=''Eugene''
Or ShipCity=''Portland''
Or ShipCity=''Seattle''
In 条件的另 外一 种形 式允许 使 用子 查询 定义 一组要比较 的 值
Select CustId,
Name
From Customer
Where ShipCity In (select Distinct City
From Warehouse)
在这个示例中 子查询生成一个结果表 对于有仓库的每一个城市 在结果表中有一
行 在 Customer 表中 如果 某行 的 ShipCity 列包含子 查询 结果 表中 的一个城市 那么才 选
择该 行 Select 语句的最终结果表只有那些客户与仓库在同一个城市中的客户 注意 In
条件的 子查 询必 须指 定只有 一 个列 的结 果表 在 In 条 件的 两 种 形式之前 还可 以指 定 Not
关键字 取反 这种 测试
对于那 些允 许有 多行子查询 的 条件 可以在 子查 询中使 用 Union 运算 符 下 面的示例
说明如 何编 码这 种类 型的测 试
Select CustId,
Name,
ShipCity
From Customer
Where ShipCity In(select ShipCity
From Customer Where CustId=246900
Union
select ShipCity
From Customer
Where CustId=687309
使用图 2-2 中的 数据 这条 语 句生 成下面 的结 果
Custld Name ShipCity
133568 Smith Mfg. Portland
246900 Bolt Co Eugene
499320 Adapto Portland
499921 Bell Bldg Eugene
687309 Nautilus Portland
890003 AA Products Portland
905011 Wood Bros Eugene
2.4.5 Like 条件
Like select
条件提 供字 符串 样式匹 配 一个 有搜 索条 件的 语 句如下
Select CustId,
Name
From Customer
Where Name Like ''%Steel%''
这条语 句将 显示 在名 称的任 何 地方 有字 符串 Steel 的客 户 包括 下面 的名 称
Ajax Consolidated Steel
Portland Steel Yards
Steel Fabricators of the Northwest
John Steeling Grocery Company
Umpqua Steelhead Fly Fishing Guides
在 Like 关键 字之 前的表达式 必须确 定一个字 符串( 例如 字符 或者 日期/时间 列 或者
字符串 函数 例如 SubString) 在 Like 关键字 之后 编码 一个 字符 串文 字 这个 字符 串提
供了将 要匹 配 的 样式 在 这个样 式中 可以 使用 百 分号(%) 表示 零个或 者多 个 字符 的 子串
(_)
下划 线 字 符表 示出 现一 个任意字 符的 子串 下面的条件测 试的名称是 有四个字 符 后
三个字 符是 ick
Name Like ''_ick''
这种样 式匹 配 Dick Rick Mick Nick 以及 dick rick mick nick kick 和 lick 这
种样式 不匹 配 ick( 太短) Ricky( 太长) 或者 rock( 没有包 含 ick)
注意 在 默认情况下 SQL Server 使用 忽略大小写不同的排序 所以样式_ick 匹配
DICK 等等 当安 装 SQL Server 时 可以 指定 这种排 列 顺序 对于 比较 运算
大写字母和小 写字母是不同的 在这种情 况下 这种样式 _ick 不匹配字符
串 DICK
还可以使用这种模式 例如 aeiou 来匹 配在 方括 号内 字 符 集 合中的 单个 字 符 或
者可 以使用一 种范围 例如 0-9 指定字符集 在本例中 是任 一 数 字 如 果把符号^
放在某个 字符集或者范 围的开始 那 么表示匹配字符 集包含 除了在方括号 之内的 字符的 全部字符 因此 ^aeiou 表示 除了 a e i o u 之 外的任意字符 ^0-9 表示 除了数 字
的任意 字符
如 果 需要 匹配% _或者 字符 那么可以把 这 种字符 放 在方括号内 例如 下面的 样
式所匹 配的 字符 串至 少两个 字 符 并且 在第 二个 或者最 后 一个 字符 上有 一个 下划线
Name Like _%[_]%
第一个_匹配 任何 一个 字符 第一 个% 匹 配零 个或 者多个字 符 [_] 只匹 配 一 个字符 最
后一个%匹配 零个 或者 多 个任意 字符
指定% 或者 字 符的 另 一种方 法是 指定一 个退出字 符 例如 下面的样式与前 一个
示例中 的样 式等 价
Name Like _%\_% Escape \
这个示例的 Escape 子句 指 定在 样式中 的\ 字符后面的任意字符都 将作为一个文字符对

2.4.6 Exists 条件
Exists 条件 是使 用子 查询 的另 一种 条件 形式 语法 是
Exists(select-expression)
如果 选择表达 式 的 结果表 中包 含 一行或者 多 行 那么 该条 件 为真 否则 为假(Exists 条
件 的 值永 远也 不会 未知) 虽然选择表达 式 的选择列 表可 以 指 定任意数量的列 但这些列 值
都被忽 略了 所 以可 以只使 用 作为选 择列表 在 Exists 条件之 前 可以 指定 Not 关键字
那么只 有当 选择 表达 式的结 果 表是 空的 该取 反条 件 的值为 真 当 且仅当 Seattle 中至少 有
一个客 户 那么 下面 的条件 为 真
Exists (Select
From Customer
Where ShipCity=`Seattle'')
这个示 例可 能不 太实 用 因为它 只是 说明 在 Seattle 是否 有客 户 在说 明如 何生成 左 外
连接的 示例 中 我 们将 会 看到搜 索条 件中 更有 意义 的 Exists 条件
Select Customer.CustId,
Customer.Name,
Null As SaleDate
From Customer
Where Not Exists
(select
From Sale
Where Sale.CustId=Customer.CustId)
在这 个 搜 索 条 件 中 使 用 的选择表达 式 称 为 相 关 子 查 询 因为 内部 的 选 择 表 达式( 在 Not
Exists 条件 之后)引用 Customer.CustId 它是在 外部的选择表达式中指定 的表 的 某个相 关 引
用列 因此 内部 选择 表 达式的 判断 与外 部 选择 表达 式的当 前行 相关 联
仔细看 看这 个示 例 有 助 于明白 相关 子查 询和 Exists 条件 的作 用 本示 例的 Exists 条
件回答这个问题 “ 有这个客户的销售记录吗?” 如果 回 答是没 有 那么这个搜索条件(Not
Exists…) 为真 并且 选择 该客 户 (回忆 一下 这个 Select 语句 是左 外连 接的第二部 分 并
且企图 选择 不匹 配的 Customer 行 )这个 Exists 条 件 生成一 个包 含该 客户 的全 部 Sales 行的临时结 果 表 测 试 该 客户是否 有任 何 销 售 如 果 这组行非 空 那么 Exists 条件为真—— 该
客户有 一个 或者 多个 销售
包含该 客户 销售 的临 时结果 表 是由 下面 的子 查询(嵌套 的 选择表 达式) 生成
Select
From Sale
Where Sale.CustId=Customer.CustId
因为 只对 Sale 行 的数量感兴趣 所以用 指定 一 个暗 示的列项 而不 是列出明 确的 列名
称 用于 这个 选择 表达 式 的搜索 条件 非常 简单 如果 Sale 行的 CustId 列包含的客户 ID 与
当前在 外部 Select 表达 式 中测试 的 Customer 行相同 那么这个 Sale 行就 包 含在选择表达式
的结果 表中 可以 考虑 用 SQL Server 执行 下面 完整 的 Select 语 句算法
For all Customer rows
Set CurCustId=Customer.CustId
Set TmpSaleResultTable to Empty(remove all rows)
For all Sale rows
If Sale.CustId=CurCustId
Add Sale row to TmpSaleResultTable
EndIf
EndFor
If TmpSaleResultTable is Empty
Add Customer row to final result table
EndIf
Endfor
尽管 SQL Server 没有必 要使 用这 种算 法执 行 Select 语句 但 这 种算法为理 解相 关子 查
询的结 果提 供了 一种 逻辑方 式
子查询可 以非常复杂 但是它们为表达不同的 搜索条 件提供了强大的 功能 现在 看 看
一个更加 复杂但是实用 的子查询用法 假设希望检索 每一个 客户的姓名和 城市 这个客 户
的订单 总 数 大于在该 城 市中所 有客 户订 单 的 平 均 数 下面的 Select 语句 检索 出希 望 得 到 的
客户清 单
Select CurCust.Name,
CurCust.ShipCity
From Customer As CurCust
Where Exists
( Select
From Sale As BigSale
Where BigSale.CustId=CurCust.CustId
Ang BigSale.TotalAmt>
( Select Avg(AvgSale.TotalAmt)
From Customer As AvgCust
Sale As AvgSale
Where AvgCust.CustId=AvgSale.CustId
And AvgCust.ShipCity=CurCust.ShipCity
使用图 2-2 中的 数据 这条 语 句生 成下面 的结 果Name ShipCity
Smith Mfg Portland
Bolt Co Eugene
AA Products Portland
仔细看 看这 条 Select 语 句 是如何 构造 的 可以 展示 Transact-SQL 的许多高 级检 索能 力
第一个 From 子句指定 结果表 中 的行 来自 Customer 表 关联 名称 CustId 用在这条 语 句
Customar
的其他 地方 限定 来自 表的这 个特 殊角 色中的 列
第一个 Where 子 句使用 Exists 条 件查 询该 客户 是否 有任 何满 足指定条件的定 单 这组
将要测试的订单由第一个子查询( 第二 个 Select 关键 字 开始的 查询) 指定 记住 当使用 子
查询时 可以 认为 SQL Server 对每个 由在 外部 Select 语句 中的 From 子句定义 的行 执行 子
查询 因此 在这 个示 例 中 对于 在 Customer 中的每一 行 执行 子查 询 然后 测试 其结 果
是否包 含任 何行
第一个 子查 询检 索 Sale 表的 行 因 为只 对 该 子查询的 结果 进行 测试 看 其是否 包含任
何行 所以 所有 列都 被检索 出 来(Select) 该 From 子句指 定由 相关 名称 BigSale 限定的 Sale
表中的行 只有在这个 子查询中检索 的行才是当前的 客户 并且 其 销 售量 大于 当 前客户 所
在城市 中的 客户 定购 的平均 数 量
Where Sale
子句 指定 两个 条件 的连 接 对于 在子 查询 结果 中的 行 这两个 条件 必须 为
真 第一 个条 件是 Sale 行的 客户 ID 必须与 当前 客户 行的 客户 ID 相同
第二个条件是 一种使用另一个子查询的基 本条件 每一个销售 总量与一系列销售的平
均总 量进 行 比 较 在这个 示例中 使用了大于(>) 测试 并且 因为 在 基 本条 件中的两 个值 必
须是标量 所 以在 这个 示 例中子 查询 返回 的这 组 行只 能 包 含一个值( 有 一个列的 一行) 通
AV G
过在子 查询 的结 果表 列项中 只 指定 合计函 数 该子 查询 检索 有一 个列 的一 行 它包含
所希望 的平 均值 然后 这个值 与列 值 BigSale.TotalAmt 进行 比较
第二个 子查 询( 即第三 个 Select 语句) 指定 计算平 均值的 这组行 这些 行来 自 Customer
和 Sale 表的 相等 连接(AvgCust.CustId=AvgSale.CustId) 但是 只有 那些 与当 前客 户在 同一
个城市中 的客户才能包 含在平均值中 为了判断这个 条件 在最内的子查 询中的 每一行 的
(AvgCust.ShipCity) (CurCust.Shipcity)
城市 与在主查询 中当 前客户 所 在的城市 进行比较
2.4.7 限定 的条 件
最后一 种条 件类 型是 限定的 条 件 其语 法形 式如 下
expression quantifier(selcet expression)
比较运 算符 可以 是任 何列在“Basic 条件”中的 一个 限定 符可 以是 关键 字 All 或者 Any
Some Any
注意 关键 字可 以用作 的同 义字
下面的 示例 检索 折扣 率大于 所 有 Portland 客户的折 扣 率的客 户行
select CurCust.CustId,
CurCust.Discount
From Customer As CurCust
Where CurCust.Discount > All
( Select CityCust.Discount
From Customer As CityCust
Where CityCust.ShipCity=''Portland''
And CityCust.Discount Is Not Null)
如果该子查询 的结果表是空的 或当前客 户的折扣率大于在子查询的 结果表中的全部
值 那么 该示 例的>All 限 定 条件为真 使用 列在 图 2-2 中的 客户 数据 只检 索到 一行
Custld Discount
499921 0.100
用在限定中的 选择表达式必须只有一个列 这种比较测试应用到选择 表达式的结果表
中的每 一个 值
一般地 有限 定符 All 的条 件是
如果选择表达 式的结果表是空的 或者对于结 果表中 的全部 值的比较测试 为真
则条件 为真
如果对 于在 结果 表中 至少一 个 值的 比较 测试 为假 则条 件为假
如果对于在结 果表中至少一个值 这种比较测 试不能 判断 并且对于在结 果表 中
至少一 个值 这种 比较 测 试未知 则条件 未知
有 Any 限定符的 条件 是
如果这 种比 较测 试对 于在结 果 表中 的至 少一 个值 为真 则 条件为 真
如果选择表达 式的结果表是空的 或者比较测 试对于 在结果 表中的全部值 为假
则条件 为假
如果比较测试 对于在结果表中的至少一个值不 能判断 并且比 较测试对于在 结果 表
中至少 一个 值未 知 则条件未 知
当编码 一个 限定 条件 时 一定要 小心 不 要把 用英 语 按照正 常方 式表 示的 条件 与 SQL All
和 Any 限定符的 指定 含义搞 混了 例如 可能 听到 有人想要“ 比 Portland 任 何客 户的折 扣率
都大的 客户”的清 单 但是 如果 使用 下面 的 Select 语句 那么 检索 到的 清单 将包 括比 Portland
客户中 最低 折扣 率大 的所有客 户
Select CurCust.CustId
From Customer As CurCust
Where CurCust.Discount > Any
( Select CityCust.Discount
From Customer As CityCust
Where CityCust.ShipCity=''Portland''
And CityCust.Discount Is Not Null)
显然这 个清 单包 括了 Portland 的一些 客户( 在 Portland 的 客户中 折 扣率不 是最 低 的那些
客户) 使用 All 限定 符( 就像 在上 一个 例子 中那 样) 检索比 Portland 任 何客户的 最 高折扣 率
大的客 户 这样 当然 就 排除了 全部 Portland 客户 这个 Select 语句 的一 种比 较简 单的 方
法使用 了下 面的 基本 条件
Select CurCust.CustId
From Customer As CurCust
Where CurCust.Discount > ( Select Max (CityCust.Discount)
From Customer As CityCust
Where CityCust.ShipCity=''Portland'')2.5 Select 的其他用 法
2.5.1 使用 Case 表达 式
Transact-SQL 提供了一 种 Case 结构 可 以有 条件 地返回 一个 值 Case 结 构的形 式如下
Case
When condition Then result-expression
1 1

When Then
condition result-expression
n n
Else result-expressio
n
End
SQL Server 按顺序 判断 每一 个条件 当找 到第 一个 为 真的条 件时 SQL Server 使用这
个表达 式的 值( 在 Then 关 键字之 后) 如果 没有 为真 的 条件 那么 使用 可选 的 Else 关键字 后
面的表 达式 如果 没有 为 真的条 件并 且没 有 Else 关键 字 那么 Case 表达 式 的值为 空 在
下面的 示例 中 使用 Case 表 达式为每一 个客 户创 建 一个 分类 值
Select CustId,
Name,
Case
When Discount > .3 Then 2
When Discount > .0 Then 1
Else 1
End As Category
From Customer
当一系 列测 试对 应于 同一个 表 达式 的不 同值 时 有一种 Case 结 构的快 捷形 式 例如
为了扩 展一 组字 符代 码 可以使 用下 一个 显示 的表 达 式
Select PartId,
Case PartType
When ''E'' Then ''Electrical''
When ''M'' Then ''Mechanical''
Else ''Other ''
End
From Part
每一个 When 条 件为 不同的 值 也可 以是 表达 式 测 试条件 PartType=value Case 表
达式可 以用 在标 量值 是有效 的 大多 数的 地方 包 括用在 Select Insert Update 和 Delete 语
句中
2.5.2 使用 子查 询作 为标 量值
在任何可以使 用标量值或者表达式的地方 都可以使用返回一列和一 行的子查询 下
面 的 示例 说明 如何 检索 每 一个客 户的 折扣 率对 所有 客 户的平 均折 扣率 比率
Select Name,
Discount, Discount/ (select Avg(Discount) From Customer)
As TimesAvg
From Customer
Order By TimesAvg Desc
这条语 句返 回下 面的 结果
Name Discount TimesAvg
Bell Bldg. .100 4.615
Nautilus .050 2.307
Smith Mfg .050 2.307
Bolt Co. .020 0.923
AA Products .010 0.461
Alpine Inc. .010 0.461
Seaworthy .010 0.461
Wood Bros. .010 0.461
Adapto .000 0.00
Floradel .000 0.00
Telez Co. .000 0.00
Udapto Mfg. .000 0.00
Ajax Inc. Null Null
甚至可以在这 种子查询中使用相关名称 这种技术 如下面的示例所 示 允许检索一
个类似 的清 单 但 根据 同 一个城 市的 客户 的平 均折 扣 率 计算 折扣 比率
Select CurCust.Name,
CurCust.Discount,
( select Avg(AvgCust1.Discount)
From Customer As AvgCust1
Where CurCust.ShipCity=AvgCust1.ShipCity) As CityAvg,
CurCust.Discount/
( select Avg( AvgCust2.Discount)
Form Customer As Avgcust2
Where Curcust.ShipCity=AvgCust2.ShipCity) As TimesCityAvg
From Customer As CurCust
Order By TimesCityAvg Desc
这条语 句返 回下 面的 结果
Name Discount CityAvg TimesCityAvg
Alpine Inc. 0.01 0.003333 3.0003
Bell Bldg 0.10 0.043333 2.3077
Seaworthy 0.01 0.005000 2.0000
Smith Mfg 0.05 0.027500 1.8181
Nautilus 0.05 0.027500 1.8181
Bolt Co 0.02 0.043333 0.4615
AA Products 0.01 0.027500 0.3636
Wood Bros 0.01 0.043333 0.2307 续表
Name Discount CityAvg TimesCityAvg
Adapto 0.00 0.027500 0.0000
Telez Co 0.00 0.005000 0.0000
Floradel 0.00 0.003333 0.0000
Udapto Mfg 0.00 0.003333 0.0000
Ajax Inc NUll 0.005000 Null
2.5.3 在From 子句 中使用select 表达 式
From
如前所 示 子句列出了 一个 或者 多个 表和 或视图 由 这 些 表或者视图 中 的行生
成一个 Select 语句 的结 果 集 还可 以在 From 子句中使用选择表达 式 然后由这些检 索 到
的行创建一个临时表 这种功能是一种用单个行连 接合计值 的简便方 式 如下 例所示 如
果客户 达到 了同 一个 城市中 客 户的 平均 折扣 率 那 么 计算给 每一 个客 户多 少折 扣
Select Customer.CustId
CustSaleTotal.SumTotalAmt,
CityDisc.AvgDisc,
CustSaleTotal.SumTotalAmtCityDisc.AvgDisc
As AvgDiscValue
From Customer
Join
(Select Sale.CustId,
Sum(TotalAmt) As SumTotalAmt
From Sale
Group By CustId) As CustSaleTotal
On Customer.CustId=CustSaleTotal.CustId
Join
(Select ShipCity,
Avg(Discount) As CityDisc
From Customer
Group By shipCity) As CityDisc
On Customer.ShipCity=CityDisc.ShipCity
注意 对于许 多查询 可以使用简单的或 者相关的子查询来生成一个 标量值 连接表
Where From
和视图 或者 使用 子句 子查 询达 到与 子句中选择表达 式 的同 样效 果
2.5.4 其它 Select 语句功 能
Transact-SQL 为选择表达 式提供了几个其他功能 为将结果集的大小限 定为一个指定
数字或 者行 的百 分比 可以 在 选 择列 项之 前 增加 一个 Top 子句 下面 的示 例说 明如 何指定
一个准 确的 行数
Select Top 10
Name,
Discount
From Customer
Order By Discount Desc这条语 句返 回下 面的 结果
Name Discount
Bell Bldg 0.01
Smith Mfg 0.05
Nautilus 0.05
Bolt Co 0.02
Alpine Inc 0.01
Seaworthy 0.01
AA Products 0.01
Wood Bros 0.01
Adapto 0.00
Floradel 0.00
在大多 数情 况下 Select 语句 将有 一个 Order By 子句和一 个 Top 子句 以使那些记 录
是某些 行中 最高 值或 者最低 值 的列 为了 返回 所选 行 的指定 百分 比 使 用下 面的形 式
Select Top 10 Percent
Name,
Discount
From Customer
Order By Discount Desc
这条语 句返 回下 面的 结果
Name Discount
Bell Bldg. 0.10
Smith Mfg 0.05
当有一 个 Order By 子句( 在大多 数情 况中) 时 还 可 以指 定所有行的 Order By 列值与包
括在 Top 数字 中的 最后 一行 或者 包括 在结 果中 的百 分比的 Order By 列值相同的所 有 行
Select Top 10 Percent With Ties
Name,
Discount
From Customer
Order By Discount Desc
这条语 句返 回下 面的 结果
Name Discount
Bell Bldg 0.10
Smith Mfg 0.05
Nautilus 0.05
With Ties 选项 是有 用的 例如 它可 避免有 特 定折 扣 率的一 些客 户包 括在 结果 中 而
有同样 折扣 率的 其他 客户却 被 排除 在结 果之 外
2.5.5 Group 子句 的Rollup 和 Cube 选项
前面 介绍 了合 计 函 数和 Group By 子 句提供 了一 种检索合 计信 息的 方式 这些标准的
SQL 功能 既 不 能 提供合并 详 细 行 和 汇 总 信息的方式 也不 能 在 同一个查询中 提 供 一 种获 取
多层汇 总信 息的 方式 Transct-SQL 有两个对 Group By 子句的扩展 选项 可 以提供 这些功
能 在Group By 列项之后 并且 在 Having 子句 之后( 如果 有) 可以指 定 With Rollup 选项
或者 With Cube 选项 With Rollup 使 SQL Server 在 分 组列表 的 每一层 增加 一个 子总 行 例
如 下面 的语 句使 用 With Rollup 选项
Select Customer.CustId,
Sale.OrderId,
Avg(SaleItem.Price) As "Avg.Sale Price"
From ( Customer Join Sale
On Customer.CustId=Sale.CustId)
Join
SaleItem
On Sale.OrderId=SaleItem.OrderId
Group By Customer.CustId,Sale.OrderId
With Rollup
当使用 查询 分析 器时 该查 询 结果如下 面 前 三列所 示
CustId OrderId Avg.Sale Price Notes
133568 234117 6,5733 Group By row
133568 Null 6.5733 Rollup summary for a CustId
246900 234116 4.0066 Group By row
246900 234120 4.2800 Group By row
246900 Null 4.1160 Rollup summary for a CustId
499320 234112 4.9000 Group By row
499320 234114 8.3400 Group By row
499320 234119 4.2800 Group By row
499320 Null 6.1971 Rollup summary for a CustId
888402 23411 7.2033 Group By row
888402 Null 7.2033 Rollup summary for a CustId
890003 234115 3.3066 Group By row
890003 Null 3.3066 Rollup summary for a CustId
905011 234118 5.9000 Group By row
905011 Null 5.9000 Rollup summary for a CustId
Null Null 5.5221 Rollup summary for all rows
注意 这个 汇总 层如 何从右 向 左汇 总 Group By 的列项 With Cube 选 项与此类似 但
Group By
对每一 个可 能的 列组 合 通过从 列项中 删除 一个 或者 多个列 创建 了 一组汇 总行
在前面 的示 例中 使用 With Cube 替代 With Rollup 将对 CustId 和 OrderId 的每一个 组 合
每一个 CustId 值 每一 个 OrderId 值和 全部 行生 成汇总 行 如果 Group By 列项有三 个 例
如 C1 C2 C3 那么 在 结果表 中有 八组 汇总 行
对每一 个 C1 C2 和 C3 值的 唯一 组合( 这 些行 是由带或不带 Cube 选项 的 Group By
)
子句创 建
对于每 一个 唯一 的 C1 和 C2 值对
对于每 一个 唯一 的 C1 和 C3 值对
对于每 一个 唯一 的 C2 和 C3 值对
对于每 一个 C1 值
对于每 一个 C2 值
对于每 一个 C3 值
对于整 个所 选行 集的 一行
注意 Cube 运算 符有令人迷惑的名 称 因为 它支 持多于三维的操作( 就 像一个立 方体
3
一样) 并且 汇总 行集 的数量 不 是 C (Group By 列数的立 方) 而是 2
如果所 有的 Group By 列都不允 许空 或者 使用 空的 组合 列剔 除全 部 行 那么 可 以指出
哪些结 果表 中的 行是 通过检 查 组合 列中 的空 值 由 Rollup 选 项或者 Cube 选项 增 加的汇总
行 否则 一 个空 组合 列 可能 包含在 有空 列的 行组 没有办 法区 分两 种类 型的 汇 总行 SQL
Server 提供了 Grouping 函数 解决 这个 问题 为了 在结果 表 中增 加区分汇总行类型的 列 对
在选择 列表 中的 每一 个组合 列 包括 一个 Grouping 函数
Select Grouping( Customer.CustId) As RollupAllCustId,
Grouping( Sale.OrderId) As RollupAllOrderId,
Customer.CustId,
Sale.OrderId,
Avg( SaleItem.Price)
From ( Customer Join Sale
On Customer.CustId=Sale.CustId)
Join
SaleItem
On Sale.OrderId=SaleItem.OrderId
Group By Customer.CustId,Sale.OrderId
With Rollup
结果如 下
RollupAllCustId RollupAllOrderId CustId OrderId Avg.Sale Price
0 0 133568 234117 6.5733
0 1 133568 Null 6.5377
0 0 246900 234116 4.0066
0 0 246900 234120 4.2800
0 1 246900 Null 4.1160
0 0 499320 234112 4.9000
0 0 499320 234114 8.3400
0 0 499320 234119 4.2800
0 1 499320 Null 6.1971
0 0 888402 234113 7.2033
0 1 888402 Null 7.2033
0 0 890003 234115 3.3066
0 1 890003 Null 3.3066
0 0 905011 234118 5.9000
0 1 905011 Null 5.9000
1 1 Null Null 5.5221第一列( 别名 是Rollup AllCustId) 的值 1 表示 没有 使用 CustId 值决定子 组 的 Rollup 汇总
行 在本 例中 只 有所 有 行的汇 总行 才在 该列 上是 1 RollupAllCustId 列的 0 表示普 通 的
Group By 行和任 何使 用 CustId 决定子组 的 Rollup 汇总 行 同样 第二个列( 别名是 Rollup-
AllOrderId) 的 0 或者 1 取 决于汇 总行 的类 型 在这 个示 例中 所有 的 Rollup 汇 总 行都将 1
作为 RollupAllOrderId 列的值 因为 没有 Rollup 汇总 行使用 OrderId 决定子 组 OrderId 是
最低层 的组 合列 只用 于 确定普 通的 Group By 组 注意 普通 的 Group By 行的 0 表示全
部 Grouping 函数 列 而生成 Rollup 行 的至 少有 一个值为 1 的 Grouping 函数列 按照 同样
的方式 可以 用 Grouping 函数 区分 使用 Cube 选项 生 成的结 果表 中的 行的 类型
2.5.6 Compute 子句
对于交 互式 检索 Transact-SQL 还有另一 种提 供分类汇 总 功能 的 特征 可以 在一 个 Select
语句之 后 包 括几 个选 择 表达式 联合 中的 一个 一个 或者多 个 Compute 子句 该子句列出
了合计函 数以及用于一 个或者多个合 计层的列 下面 的语句 生成了对于每 一个客 户有分 类
汇总的 Sales 行清 单和 一个 总和
Select CustId,
OrderId,
TotalAmt
From Sale
Order By CustId,OrderId
Compute Sum(TotalAmt) By CustId
Compute Sum(TotalAmt)
当使用 查询 分析 器时 查 询结果 如下 所示
CustId OrderId TotalAmt
---------- ----------- --------
133568 234117 550.0000
sum
--------
550.0000
CustId OrderId TotalAmt
---------- ----------- ---------
246900 234116 678.0000
246900 234120 399.7000
sum
---------
1077.7000
CustId OrderId TotalAmt
---------- ----------- ---------
499320 234112 35.0000
499320 234114 78.9000
499320 234119 201.0000
sum
----------
314.9000CustId OrderId TotalAmt
---------- ----------- ---------
888402 234113 278.7500
sum
----------
278.7500
CustId OrderId TotalAmt
------------ ----------- ------------
890003 234115 1000.0000
sum
--------
1000.0000
CustId OrderId TotalAmt
---------- ------------ ---------
905011 234118 89.5000
sum
-------
89.5000
sum
-------
3310.8500
16 行受影响
Compute 子句 可以 包含 任何 列在 前面 表 2-3 中的 合计函 数 Compute 子句中可选的 By
列项定 义 break 层次 并且 必须 包含 列在 Order By 子句中的 一个 或者 多个 列或 者表 达式
By 列必须从 Order By 子句中的 第一 项开 始 并且按 照从 左到 右的 顺序 不能 跳过 任 何项
就像前 面的 示例 那样 可 以使用 多个 Compute 子句 来得 到几 个层 次的 合计 信息
2.5.7 Into 子句
Select 语句 的主 要用 途是 检索 数据 Transact-SQL 有一种扩 展 允许用 Select 语句检 索
到的行 创建 一个 新表 新 表的名 称由 Into 子句 指定 Into 子句紧 跟在 Select 列项之 后
Select
Into CustomerSeattle
From Customer
Where ShipCity=''Seattle''
新创建 的 表 有与 在 Select 列项 中 指 定的 相 同 的列 并且 有来自 结果表中 的行 包括行
选择或者 合计的影响 这 种功能 的主要目 的是创建一个临时表 它可 以用 于查 询 会话 过 程
中的后 续 检 索 如果 希 望使 用 Select 语 句将行增 加 到一 个永久 表中 那么 必须 使 用下面 的
系统存 储过 程调 用打 开 Select into/bulkcopy 选项
sp_dboption AppDta select into/bulkcopy true
在 SQL Server Enterprise Manager 中 右击 数据 库 从 弹出 的菜 单中选择 Properties, 在
Options 标签 上 Select Into/bulk copy 复选框 也可 以设 置这 个选 项
注意 这个新表没有任何可能在源表上定义的约束 这是一个通常应该使用 Create
Table 而不 是 select 来创 建新 的永 久 性 表的 原因 为了从 一个 或者 多 个 表中将数据复 制到一个 已有 的表 中 使 用 带有子 查询 的 Insert 语句 下面论述 此项 内容
2.6 Insert 语句
SQL 有三种 可以 用于 修改表 数 据的 重要 的 DML 语句 Insert Update 和 Delete 所有
这三种语 句都可以 修改表中的一行或 者表中的一个行 集 它们 都 不 能在一条语句 中修改 多
个表 还 可以 通过视图 修 改数据 然而 在一 个视图 上运行 的 DML 语句一次只 能修 改基
表 所有 这三 种 DML 语句允许 使用 Where 子句指 定 将要插 入 修改 或者 删除 的 行集 在
这些语 句中 的 Where 子 句的一 般形 式 与 Select 语句 中 Where 子 句的形 式相 同
2.6.1 基本 Insert 语句
为了 向表中增 加 一 个新 行 可以使用 Insert 语句 例如 为 了 增加 一 个 新客户 输入
下面的 语句
Insert Into Customer
( CustId,
Name,
ShipCity,
Discount)
Values ( 678987,
''Atlas Inc'',
''Portland'',
Null)
括号中的列项跟在表名或者视图名之后 Va l u e s 子句 指定 新值 列 表 这些 值也在括号
中 与列 项中的相应列 对应 可以省 略列项 在这 种 情况下 暗示的列项 是全部 列 其顺
序是在 Create Table Alter Table Create View 或者 Alter View 等 DDL 语句中定义的 顺 序
然而 省略 列项 并不 好 因 为 它容 易出 错并且 可 读性比较差
在插入的列项 中 必须包 括系统不能确定其值的任 何列 如果一 个列是用 Identity 关
键字定 义的 或在 Create Table 或者 Alter Table 上 指定了 Default 子句 或是一 个 TimeStamp
数据 类 型 或者 允许 空 那么可以省略这个列 如果一 个省略 的列 有一 个明 确的缺省 值( 在
第 1 章中 描述 了) 那么 使 用该缺 省值 否则 SQL Server 为 Identity 列插入下一个 增 量 identity
值 为 TimeStamp 列插入当 前的 时戳 值 或者将 列设置 为 空
可以使 用任 何有 效表 达式作 为 列的 值 如上例 所示 可以使 用 Null 关 键字明 确 地设置
某列为 空 当然 设置 为 空的列 不能 用 Not Null 子句 定义
还可以 在 Insert 语句的 值项 中使 用 Default 关 键字指 定 应该使 用某 个列 的缺 省值( 或者
空值) 指定 Default 的 任 何列要 么有 一个 缺省 值 如 前所述 要么允 许空 值
2.6.2 多行 Insert 语句
多行 Insert 语句 从一个表 中 将数据复 制 到另 一 个 表 并且实 现了最接 近于 关系赋值运
算的 Transact-SQL 等价运算 多行 Insert 语 句的 目 标 表必须 已经 存在( 不像 有 Into 子句的 select
语句那 样 Insert 语句 不能创 建 新表) 例如 下面 的 Insert 语句从 一个 旧版 本的 Customer
表中将 所有 行复 制到 一个新 版 本的 表中 它 有一 个附加 的 ShipState 列Insert Into Customer
( CustId,
Name,
ShipCity,
ShipState,
Discount)
select CustId,
Name,
ShipCity,
,
Discount
From CustOld
最初 新表 中的 所有 行都有 一 个空 白的 ShipState 列 因为 Select 语句 的结 果表包 括 空
格文字 作为 在其 选择 列表中 倒 数第 二个 元素
虽然 该 Insert 语 句只可以 在一个表 中 增 加行 但 是插入的 行 可 以来 自 多 个表 例如
下面的 Create Table 和 Insert 语句复 制了合 并 的客 户和销 售 信息
Create Table SaleWithCust
( OrderId Int Not Null,
TotalAmt Money Not Null,
Name Char (30) Not Null)
Insert Into SaleWithCust
( OrderId,
TotalAmt,
Name)
select OrderId,
TotalAmt,
Name
From Customer,Sale
Where Customer.CustId=Sale.CustId
在 Insert 语句执行之后 对在 Customer 或者 Sale 表中数据的改变并不反映在
SaleWithCust 表中 多行 的 Insert 语句 与 视图不 同 它从 From 子句 中引用的表中 拷 贝 数

就像前 面两 个示 例那 样 可以在 一条 Insert 语句中 使用 Select 语 句指定 将要 插入 的行
这种嵌 套的 Select 语句 可 以使用 在前 一节 中讨 论的 From Where Having Group By Union
和 Order By 子句(但是 不包 括 Into 或者 Compute 子句)
Transact-SQL 还允许指 定嵌 套的 Execute 语句 它 调 用一个 存储 过程 为多 行 Insert 语句
生成结 果表 如果 有一 个 存储过 程 CalcCustCreditRating, 它使 用 Customer 和 Sale 表生成 一
个有客 户信 贷比 率的 结果表 那么 可以 使用 下面 的语句 创 建和 加载 这个 新表
Create Table CustCreditRate
( CustId Int Not Null,
CreditRate Int)
Insert Into CustCreditRate
( CustId,
CreditRate) Execute CalcCustCreditRating
CalcCustcreditRating 必须提供一个有两个列的结 果 集 这 两 个列与 Insert 语句 的列项
相对应 存储 过程 在本 章 后面讨 论
2.6.3 在视 图中 插入 行
为了通过 视图插入行 这个视图 必须不包含任 何由文 字或者表达式定 义的列 视图 中
的列必须是基表中的简单 列 或者是可插入的视图 如 果 一个视图分散在多个基表 中( 例如
视图是 一个 连接) 那么 Insert 语句只 能查 看在 一个 基表 上定 义的 列 当使 用视图 时 任何
没有显示 为可修改的视 图列的 基表列 必须有一个缺省 值 如 前所述 或者允许空 并且 新
行为省 略的 列获 得缺 省值或 者 空值
注意 尽管 视图 的 Insert 规则看 起来 有些 复杂 但是 实际 上大 多数 的 Insert 操作要 么
使用一个基表 要么使用 在一个 基表上的一些 或者 全部列的视图 对于这些情
况 要了 解的 主要 规则 是 对省略 列的 限制
2.7 Update 语句
在 Update 语句 的 Where 子 句中使 用 某个 指定 行的 主 键值并 且为 一个 或者 多个 列 赋于新
值 可以 修改 该行 例如 下面 的 Update 语 句改 变客户 的姓名 并 且对某个客 户的 当前 折
扣率增 加两 个百 分点
Update Customer
Set Name=''Wood Products''
Discount=Discount+.02
Where CustId=905011
Set 关键 字之 后是 一个 或者多 个 列的 赋值 形式
column-name=expression
使用搜索条 件 指 定多个行 可 以 修 改一组行 下 面 的语 句给所 有 的 Portland 客户提供
百分之 十的 折扣 率
Update Customer
Set Discount=.10
Where ShipCity=''Portland''
如果没 有指 定 Where 子句 那么 修改 指定 表中 的全 部行
注意 这种 Update 语句 也 称作搜 索的 Update 因为 SQL Server 搜索 将要 修改 的全 部
行 使用 Transact-SQL 游标( 在本章 后面 的 存储过 程 一 节 中讨论) 还可以
定位修改 这时 首先检索出来希望修改 的行 然后指定 Where Current Of
cursor-name 来修 改当 前行
使用 Null 关 键字 可以 将允许空的列设置 为空
Update Customer
Set Discount=Null
Where ShipCity = ''Portland''
类似地 使用 Default 关键 字 可以设置 有缺 省值 的 列( 或允许 空 的 列) 为其 缺省值( 或者
为空值)Update Customer
Set Discount=Default
Where ShipCity = ''Potland''
2.7.1 在Update 语句 中 使用子 查询
在某个列赋值 右端上的表达式可以是一个 标量子查询 即返回一个单 行和一个单列的
查询 下面 的示 例将 Portland 客户的 折扣 率 设置 为全 部客户 的平 均折 扣 率
Update Customer
Set Discount = (select Avg (Discount) From Customer)
Where ShipCity = ''Portland''
在列的赋值中 使用一个相关子查询 可以 根据一个表中相关行的值设 置另一个表中行
的值 下 面的 示例 说明 了 如何把 价格 项从 单独 的 Price 表 中复制 到 Item 表中
Update Item
Set ItemPrice = (select ItemPrice
From Price
Where Price.ItemId = Item.ItemId)
Where Exists (select
From Price
Where Price.ItemId = Item.ItemId)
注意这 个示 例是 如何 使用 Exists 条件 在 Items 表中限制这些 行 的变 化 这些 行在 Price
表中有 一个 匹配 行 如 果 没有这 种条 件 所有 的 Item 行都将被 修改 没有 匹配 Price 行的
那些行 将其 价格 设置 为空
注意 Transcat-SQL 有一个对 Update 语句 的非 ANSI 标准 的扩 展 允许 From 子句作
为 Update 语句 的一部 分(不只 是在 子查 询中) 使用这 种扩 展 前面 的示 例可 以
写成下 面的 形式
Update Item
Set ItemPrice = Price.ItemPrice
From Item,Price
Where Price.ItemId = Item.ItemId
Update 语句可以使用 有 子查询 的搜索条件检索将 要 修改的 行 下面的语 句 对 那 些 当 前
的折扣 率低 于百 分之 十并且订单总数超过 1000 的客 户提高 到百 分之 十的 折扣 率
Update Customer
Set Discount = .10
Where (Discount<.10 Or Discount Is Null)
And 1000 < (select Sum(TotalAmt)
From Sale
Where Sale.CustId = Customer.CustId)
在 Update 语句 的 Where 子句 中 可 以使用 在 select 语 句中讨 论的 任何 条件 甚至 可以
用将要 修改 的表 作为 某个子 查 询的 基表 因此 下 面 的子查 询是 有效 的
Update Customer
Set Discount = .10
Where Discount Is Null
Or Discount < ( select Avg(Discount)
From Customer
Where ShipCity = ''Portland'')
这条 Update 语句 为 那些 没 有折扣 或者 折扣 率低 于 Portland 客 户的平均 折扣 率的 客户
提供百 分之 十 的 折扣 率 在 本例中 SQL Server 计算子 查 询的平均 值 并用该平均 值 作 为
Update 中 将要 修改 的全 部 行 的测试值 注意 虽然 对 Portland 客 户的单 个修 改可 能改 变平
均值 但是 这种 改变 不影响 在 Update 中所 选择 的行
2.7.2 修改 多个 表
为了 修改 多个 表 必须 使 用多条 DML 语句 例如 为 了增加 雇员 和 承 包人 的小 时工
资 可以 使用 下面 两条 语 句
Update Employee
Set HourlyRate = HourlyRate 1.05
Update Contractor
Set HourlyRate = HourlyRate 1.05
当修改主键 唯一性键或者外键列时 必 须考虑该表存在的键值约束 例如 为了改
变一个 客户 的 ID 必须 保证 在 Customer 表中以 及在所 有 用 CustId 作为外键 的表中 改 变
CustId 列值如 果不 存在 外键 约束 那么只需 使用 多个 Update 语句 例 如下 面的示 例
Update Customer
Set CustId = 123789
Where CustId = 888402
Update Sale
Set CustId = 123789
Where CustId = 888402
但是如 果 Sale 表有 一个 指 定 CustId 列的 外键 约束 那 么这两 条语 句都 会引 起错 误 因
为每一 个语 句本 身都 将产生 不 匹配 的 Sale 行 一种 解决 方案 是插 入一 个新 的 Customer 行
将 Sales 行改为 引用 这个 新的 Customer 行 然后 删除旧 的 Customer 行
Insert Into Customer
( CustId,
Name,
ShipCity,
Discount)
Select 123789,
Name,
ShipCity,
Discount
From Customer
Where CustId = 888402
Update Sale
Set CustId = 123789
Where CustId = 888402
Delete
From Customer
Where CustId = 8884022.8 Delete 和 Truncate Table 语句
为了从 一个 表中 删除 一行 输入 如下 所示 的 Delete 语句
Delete
From Customer
Where CustId = 905011
使用指 定多 行的 搜索 条件 可以 从一 个表 中删 除一 组 行
Delete
From Customer
Where ShipCity =''Portland''
注意 就像 Update 语 句一样 这种 Delete 语句也 称 为搜索 删除 Transact-SQL 还有定
位删除 这些 内容 在 存 储过程 一节讨 论
用于 Delete 语句 的搜 索条 件也 可以 包含 子查 询 就像 在 Update 语 句示例 中描 述 的那样
为了删 除与 Sale 行没 有关系 的所 有客 户 可以 使用 一个 有 Not Exists 条件 的 Where 子句和
一个相 关子 查询
Delete
From Customer
Where Not Exists ( Select
From Sale
Where Sale.CustId = Customer.CustId)
注意 Transact-SQL 有一个 Delete 语句 的非 ANSI 标准 扩展 允许 From 子句作为 Delete
语句的 一部 分(不仅 仅是 包 含在子 查询 中) 总之 子查 询是首选 的 技术
2.8.1 清除 整个 表
输入一 个没 有 Where 子句 的 Delete 语句 可以有意 或 者无意 从表 中清 除全 部行
Delete
From Customer
注意 在从 表中 清除 全部行之 后 该表 仍然 存在 它只是 一个 空表 使用 在第 1 章讨
论的 Drop 语句 可以 从系 统表 中删 除一 个表
SQL Server 提供的 Truncate Table 语句 可 作为一种 快 速清除 表中 数据 的方 法 下 面的语
句在功 能上 等价 于前 一个 Delete 语句
Truncate Table Customer
注意 不像 Delete 语句 Truncate Table 语句 不把 删除 的行 放在 事务 日志 中 因 此不能
撤销此 语句 Truncate Table 语 句 也不能激 活 该 表的 delete 触发 器 不能 在外 键
约束中 作为 父表 引用 的表上 使 用 Trancate Table 语句
2.8.2 从多 个表 中删 除行
就像在 Update 语句中 讨论的 那 样 如果 希望 从多 个表中 删 除行 必须 执行 多个 Delete
语句2.9 并行修 改和表锁
当两个 NT/2000 Server 进程 访问 同一 个基 表时 一个 进程 的行 修改 可能 与另 一进 程的
检索或 者修 改相 冲突 例如 如果 一个 进程 执行 下面的 Select 语句
Select Avg (Discount)
From Customer
而另一 个进 程正 在执 行修改 Discount 列的语 句
Update Customer
Set Discount = .10
Where ShipCity =''Portland''
第一个进程可能得到一个基于一些 Portland 客户的旧 Discount 值而其他客户的新
Discount 值的 平均 值 SQL Server 自动采 取一 些措 施 防止 这两 个进 程检 索和 修改 单个行
的冲突
2.9.1 如何 防止 访问 冲突
对于 Select 和 Update 语句 Transact-SQL 有一些扩 展 为明 确锁 定基 表以防止冲 突 访
问提供 了一 种方 式 在 这 两种情 况下 在引 用的 表名后 面 使用 With 关键 字 可以 使用表提
示 下面 的语 句防 止 Select 语句 冲突 修改
Select Avg( Discount)
From Customer With (TabLock)
在 Select 语句 中 TabLock 提 示 指定在 Select 语 句 执行过程 中 保持对整 个 表 占有共 享
锁 共享 锁防 止其 他修 改 访问 但是 允许 其他 阅读 访 问
另外 Update 语句 可以 使 用带有 TabLockX 表 提示的 From 子句来占有 排它 锁 这可
防止另 一个 进程 对表 进行任 何 类型 的访 问 下面 的语句 保 证在 Update 语 句执行 期间 没有
其他用 户访 问 Castomer 表
Update Customer
Set Discount = .10
From Customer With (TabLockX)
Where ShipCity = ''Portland''
总之 应该尽 量用最短的时间锁定表 因 为表锁可能堵塞其他进程对 表进行 正常的访

2.9.2 如何 维持 数据 库的 一致 性
当修改 SQL Server 数据库时 另外 一种 考虑 是当 修改 多行 时 维 持数 据库的 一致 性
假设输 入下 面的 Update 语句 对于 那些 有非 空折 扣率的 所 有客 户提 高折 扣率
Update Customer
Set Discount = Discount + 0.001
Where Discount Is Not Null
为了执 行这 条语 句 SQL Server 检索 测 试 并 可能修 改每一 行 如 果在处 理 了一些 不
是全部 Customer 行 后 执 行这条 语句 的 进程突 然中 断( 例如 断电) 那么 Customer 表将处于不一致的状态 虽然有些行 增 加 但 是 有 些行没有 增 加 Update 语句 也不 能 重 新 输 入
因 为 这将 为那 些在 前面 没 有完整 执行 更新 的过 程中 已 经修改 的客 户增 加额 外的 折 扣率
SQL Server 为事务 的完 整性和恢复 性提 供了 一 种 功能 保证多行 事务 的执 行 要么全部
完成 要么 全部 没有 完成 SQL Server 确保 由 Update 语句 在完 成 和 正在提 交 之前突 然
失败造 成的 全部 行的 变化自 动 由 SQL Server 撤消 即使因突然断电关 闭 系 统也是如 此 在
这次失 败的 修改 之后 表 中的所 有行 都恢 复到 开始 修 改之前 的状 态
注意 这种 SQL Server 功能 没有 什么 奇妙 当修 改一 个表 时 SQL Server 会在该行
修改之 前 在事 务日 志(用 于这种目的 的 SQL Server 文件) 中存 储 每一 行的改前
图像 拷贝 如 果整 个修 改不能正 常完 成 SQL Server 用这些改 前图 像将 每一
行改回 到其 预先 修改 的值
在默认 情况 下 SQL Server 运行在自动 提交 事 务 模式下 在这 种模 式下 把每一个单
独的 Update Insert 或者 Delete 语 句 作为一种 要么 全 部完成 要么 全 部没 有 完成的事务 当
这种语 句完 成时 这些 变 化就是 永久 的
当以自 动提 交事 务模 式运行 时 可以 定义 一个 明确 的 事务 执行 一个 Start Transaction
语句 其后是 希望包括 在 该 事务 中 的 语句 明 确结束事 务时 可以 用 Commit 语句使改变
永久化 或者 使用 Rollback 语句取 消这 个事 务中 的变 化
如果不使用自动提交的事务模式( 有或 者没 有明 确的 事 务) 可以将事务模式设置为 隐
含的事 务模 式 使 用隐 含 的事务 模式 在一 个会 话中 列 在下面的语 句中 第一 个 SQL 语句
开始第 一个 事务 结束 某 个事务 的 Commit 或者 Rollback 语句暗示开始 下一个事 务
Alter Table
Create xxx
Delete
Drop xxx
Fetch
Grant
Insert
Open
Revoke
select
Truncate Table
Update
隐含的 事务 模式 符合 ANSI 标准的 SQL 方法 可以 使用 下面 的语 句将某个 指 定的 会 话
改为隐 含的 事务 模式
Set Implicit_Transactions On
事务可以 用于将多个修 改语句组合到一个操作 中 以 便 使 在 事务中全 部修改语句做 出
的所有数 据库变化要么 发生 要么没 有发生 考虑一 个经典 的银行事务 在这 个 事务中
从一个 储蓄 帐号 转帐 一笔金 额 到一 个支 票帐 户 这种事 务 要求 至少有两个 Update 语句 并
且要么两 条语句全部完 成要么两条语 句全都没有完整 这一 点非常关键 使用 一 个明确 的
事务 这些 语句 的顺 序如下
Begin Transaction
Update Saving Set Balance = Balance-100.00
Where AccountId = 123987
Update checking
Set Balance = Balance + 100.00
Where AccountId = 123987
Commit
如果决 定取 消还 没有 提交的 事 务更 新 只需 执行下 面 的 Rollback 语句
Rollback
使用隐 含的 事务 模式 时 不能使 用 Begin Transaction 语句 因为 事务 的开 始由 会话 中
的第一 条语 句或 者前 面的 Commit 或者 Rollback 语句 定义
Commit 和 Rollback 语句 一 般 都可以用 在存 储过 程和 HLL 程序中 而不 是交 互式 执行
特别 是 Rollback 语句经常用于当检测到错误时 取消未提交的修改 Rollback 语句使 用
的基本 逻辑 如下
--Begin transaction (explicitly or implicitly)
Update Saving
Set Balance = Balance -100.00
Where AccountId =123987
If Error
Rollback
Else
Update Checking
Set Balance = Balance +100.00
Where AccountId = 123987
If Error
Rollback
Else
Commit
EndIf
EndIf
这个事 务只 有在 两个 帐户转 帐 都成 功完 成时 才能 提交
注意 使用 明确 的事 务 可 以有选择 地在 Begin Transaction Commit 和 Rollback 语句
之后 指 定 一个事务 名 称 但 这并没 有 任何实际的影 响 还可以嵌 套 事 务(例如
在触发 器中) SQL Server 忽略 除了 外部 的 Commit 或者 Rollback 语 句之外的 全
部语句 然而 嵌套 的事务 要 求 Begin Transaction 和 Commit Rollback 操作
成对 如果处理不正确会引起错误 总之 事务 的开始步和 结束 点应 该在应用
程 序 中必 须要 么全 部完 成 要么全部不 完 成 的操作初 始 化和确 定其 完成 状态
2. 10 存 储 过 程
除了上 面描 述的 单个 DML 语句外 SQL 有许 多语 句允许编写存储过程 存储 过程 是
Transact-SQL 语句编译 而成 的对 象 可以 用 EXECUTE storedprocedurename 语句( 或者在其
它数据 库界 面中 的函 数 例如 ODBC)运行它 存储 过程 可以 返回 一个 或多 个结果 集 整型
返回代 码 OUTPUT 自 变量 或参数 存 储程序有 许多 优 点 高速 度 可重 用性 商业 逻 辑封装 保护 尚未 优化 的查询 减少网络拥挤 以及安 全 性( 因为只 需对存储过程授予许可
而对它 访问 的对 象不 需授予 许 可) 存储 过程 是 Transact-SQL 开发人员 与数据库交互操作
提高数 据库 性能 和可 靠性的 基 础
Microsoft SQL Server 能提供 永久 或临 时存储 过程 临 时存储 过程 存储 在 tempdb 系统
数据库 中 在 一 个存 储过程中 可以 使用 任何 SQL 语句 但 是 不 包括下 面的语 句 Create
Default,Create Procedure,Create Rule,Create Trigger,Create View 存储过 程非 常类 似任 何 HLL
过程—— 它可 以有 输入 输 出参数 本地变 量 数字 和 字符计 算和 赋值 数据库操作(DDL
和 DML) 以及控制 执行流程 的 逻辑 下面 是一 个 创建 存储过 程的 简单 示例
Create Procedure ListCustWithDiscount
@MinDiscount Dec(5,3)
As
select
From Customer
Where Discount >= @MinDiscount
存储过 程总 是在 当前 的数据 库 中创 建 为了 执行 这个过 程 使用 下面 的语 句
Execute ListCustWithDiscount .1
注意 当使 用查 询分 析器或 者 OSQL 时 如果 存储 过程 调用 是要执行 的 唯 一语句 或者
是批语 句中 的第 一条 语句 那么 关键 字 Execute 是可 选的
存储过 程最 大的 嵌套 层数是 32 内置 函数@@NestLevel 提供了当 前的 嵌套 层
当定义 存储 过程 时 存 储 过程名 称在 Create Proedure 关 键字之 后 参数 声明 在存 储过
程名称 之后 As 关键字 表示 存储 过 程 主体 的开 始 存储 过程 主体 是 一 个或 者多个 SQL 语
句 存储 过程 可以 有选 择 地返回 一个 整数 值(类似于 HLL 函数返回一 个值 的方 式) 可以 定
义输出 参数 将数 据返 回到调 用 者 下面 简要研 究这 些 话题
注意 Transact-SQL 允许有选择地使用一个名称和数字区分一个存储过程( 例如
ListCust 1) 当 执 行用数字创建的存 储 过 程 时 也必须包括该数 字 虽然 这种
方法允 许在 一个 Drop Procedure 语句中 删除 有相 同名 称 和 不同数字 的全 部
过程 但是实践证明 这 种命名 不好 我 们建 议对 于 全部存 储过 程使 用唯 一的
描述性 名称( 没有 数字)
2.10.1 修改 和删 除存 储 过程
Alter Procedure 语句 允许 改 变存储 过程 的代 码 而 不必 改变 已经 授于 该过 程的许可 Alter
Procedure 语句 的语 法类 似于 Create Procedure 语句的 语法 下 面的示 例说 明如 何修正存 储
过程 ListCustWithDiscount:
Alter Procedure ListCustWithDiscount
@MinDiscount Dec(5,3)
As
select
From Customer
Where Status
And Discount .= @MinDiscount
使用 Sp_rename 系 统存 储过程可以 重新 命名 一 个 存储过 程 它带 三个 参数 旧名称
新名称和对象类型( object 是存储过程的对象类型) 下面的示例重新命名
ListCustWithDiscount 过程
sp_rename ''ListCustWithDiscount'', ''ListCustomerWithDiscount'', ''object''
无论何 时改 变存 储过 程使用 的 表或 者当 SQL Server 启动 之后 第一 次运 行存储 过程 时
SQL Server 自动编 译 和 优化 该存储过程 如 果增加了一 个 新索引 并且 希望强制重 新 编 译
来利用 该索 引 可以 使用 sp_recompile 系统 存储 过程 如下 所示
sp_recompile `ListCustWithDiscount''
该示例 标记 ListCustWithDiscount 过程 以便 下次 调用 它时重 新编 译
可以使 用 Drop Procedure 语句 删除 存储 过程
Drop Procedure ListCustWithDiscount
2.10.2 显示 有关 存储 过 程的信 息
有三个 系统 存储 过程 可以显 示 有关 存储 过程 的信 息
sp_help procedure-name 显示 该存 储过 程的 所有 者和 创建 的时 间
sp_helptext procedure-name 显 示 该存储 过程 的源 代码
sp_depends procedure-name 显示该 存储 过程 引用 的对 象清 单
如果丢 失了 最初 用来 创建存 储 过程 的源 代码 那么 sp_helptext 系 统存储 过程 特 别有用
2.10.3 存储 过程 中的 延 迟名解 析性 能(Delayed Name Resolution Behavior)
由于允 许 SQL Server 开发人 员创 建在 Transact-SQL 代码中 并不 存在 的存 储过 程 批处
理文件 或触 发程 序及 引用对 象 延迟 名解 析可 节省 Transact-SQL 编程人员 的大量 时 间和 精
力 在较 老的版本 中 Transact-SQL 编程人员必须按特定的顺序创 建对 象 例如 在 6.x
版本中 当创 建存 储过 程 时 存储 过程 在创 建脚 本的 Transact-SQL 代码中所引用 的 对象 必
须在分析该存储过程( 即 句 法分析) 和创 建存 储过 程对 象时 存在 于数 据库 上 如 果 在数据 库
上找不 到存 储过 程所 引用的 对 象 就无 法创 建存 储过程 对 象 在以 前的 版本 中 SQL Server
会返 回 引用 对象 不存 在 的错误声明 为了创建引 用 某个 对 象 的 存 储 过 程( 也可 以 是触发
程序) 就必 须创 建该 对象 这是 非常 费时 的 也不 符 合逻辑 的循 环
在 Microsoft SQL Server 7 及 2000 中 创 建存 储过 程 时若引 用对 象不 存在 当分析该
存储过程( 分析 语句 的合 法 性) 时 即使 在数 据库 上找 不到 存储 过程 或触 发程 序 中 引用的 对
象 SQL Server 也允 许 创建 存储过程 并将 它放到数据 库 上 在以 后的 解析运行阶 段 会 解
析名称 解析阶段是另 一个合法性分 析阶段 它发 生 在运行 期间 而不是 对象创 建期间
如果存 储过 程在运行 期间引 用了对 象 这 可能 是几 天 之后的 事情 而 被引用的对 象仍 未 创
建 SQL Server 将发出 一个 运行错 误的 信息 因为 存 储过程 通不 过解 析阶 段
这样会 使 Transact-SQL 的开发 更加 容易 因为 在创 建存 储过 程时 可能 有许 多对象 不 存
在 但是 它们将在执行 存储过程的运 行期间创建 在 解析阶 段的运行期间 可 以 阐明查 询
计划(Query plan)
Microsoft SQL Server 用表中 的数 据量 表 中出 现的 索 引等信 息来 建立 查询 表 现有 引
用对象的 数据必须在解 析阶段存在 但是因为在这一 阶段并 不实际执行存 储过程 对象 所
以引用 对象 名不 必存 在于对 象 创建 语法 验证 阶段
SQL Server Optimizer 必须有关于 现有(existing) 对象的信 息 才能 阐明 该查 询计 划 并把该计 划放 入 procedure cache(程序高 速缓 存)的内 存 中如图 2-5 所示 然后 Microsoft SQL
Server 使用该 查询 计划 执行 存储 过程 对象
图2-5 存储过程和触发器的新延迟名称解析
2.10.4 Alter Procedure 语句
Microsoft SQL Server
有关存 储过 程的 另一 个特 点是 它能 改变 存储 过程 对象 而不 必
从数据库和 syscomments 系统表中 删去 它来 改变 存储过程 对象 改 变 存储过程的能力是非
常重要的 因为存储过 程和触发程序 都带有其他引用 和许可 在删除对象 时会销 毁它们
只改变对象而不是删除它将保留 与该 对象 相关的引用 和许可 下表展示了谁能使用 Alter
Procedure 语句 并解 释了 ALTER PROCEDURE 的每 个参数
Alter Procedure 的参数 描述
该参数是用户希望改变的程序名
Procedure Name
该参数用来组合同名的存储过程 它是一个可选参数
Number
此参数是 用来把值 传递给 存储过程 的自变量 在该 自变量名 前使用@
Argument Name
符号 这表明用户在执行时必须把 值传递给存储过程 用户自定义 的
存储程序可以有 0 至 1024 个自变量
指定该参数的数据类型 唯一不能传递的数据类型是 image 数据类型
Data Type
可以为该自变量提供一个缺省值
Default
指定自变量可用于执行存储过程后的返回值
OUTPUT
查询计划不能保存在内存中 以便 下一次执行该存储过程时使用 用
RECOMPILE
户每次要求执行存储过程时都会生成新的查询计划 必须使用
RECOMPILE 参数 如果没有给定重新编译参数 则创建存储过程时
不会采用重新编译设定的缺省值
存储过程用加密格式存储于数据库的 syscomments 系统表中 如果要
ENCRYPTION
使用加密 必须在 alter 中使用该参数 必须明确给定 ENCRYPTION
参数 如果没有给定加密参数 则 不会采用创建存储过程时加密设 定
的缺省值 续表
Alter Procedure 的参数 描述
在创建或改变作为过滤器的存储过程时 可使用 FOR
FOR REPLICATION
REPLICATION 且只有通过复制才能使用 该参数不能与 WITH
RECOMPILE 选项一起使用
单词 AS 指明一个或多个 Transact-SQL 语句 它们组成随后的存储
AS
过程
这些语句是存储过程的实际 Transact-SQL 代码
SQL Statements
ALTER PROCEDURE 的用法
Stored Procedure Owner
Data Definition Language Administrators
DBO
ALTER PROCEDURE
语 句 的语法 如下
ALTER PROC EDURE procedure name ;number
({@argument_name } data_type = default OUTPUT )
,...
WITH {RECOMPILE | ENCRYPTION | RECOMPILE, ENCRYPTION}
FOR REPLICATION
AS
Transact-SQL statements ...
2. 11 存储过程 参数
存储过 程最 多可 以有 1024 个参 数 每 一个 参数的声明有下面的 基本 形式
@parameter-name datatype
@ Unicode @ $ # _
参数名 称以 开始 以 后 的字符 可以 是 字母 数字 或者 符号 参
数的名 称不 应该 以@@ 开始 因为 SQL Server 把 这 种 记号用 于一 些内 置的 函数 参数可 以
是列允 许的 任何 数据 类型(参见第 1 章) 如果有 多个 参数 用逗 号将 参数 声明分开
如果当调用存 储过程时没有提供参数 可 以定义一个缺省的输入值 可以为 上一个示
例提供 一个 缺省 值 它 将 选择 折扣率 大于 零的 全部 行 如下 所示
Create Procedure ListCustWithDiscount
@MinDiscount Dec(5,3) = 0.001
As
select
From Customer
Where Discount >= @MinDiscount
.001
使用 这个定义 只用下 列语句 就可 以 调用这个 存储过 程 这条语句 等价于用 作为
参数调 用该 存储 过程
Execute ListCustWithDiscount
所有参 数都 可以 用作 输入参 数 为了 用一 个参 数作 为 输出参 数 在其 声明 上增 加 Output
关键字 在缺 省值 的后 面 如下示 例
Create Procedure GetCustDiscount @CustId Int,
@Discount Dec(5,3) Output
As
Set @Discount =
( select Discount
From Customer
Where CustId=@CustId)
注意 在这个 示例中 如何使用一个标量 子查询 在表列中赋一个值 当该存储过程返
回时 输 出参数有其最 后赋予 的值 所有存储过程的 参数都 允许空 也就 是说 可以输入
输出空 值
当调 用有 一个 输 出 参数 的 存储过程时 必 须为该参 数 提供一 个 变 量 在 Execute 语句
上的参 数之 后使 用 Output 关键字
Execute GetCustDiscount 123789,
@CustDiscount Output
在 Execute 语句 上 只 要存 储过 程参 数声 明了 一个缺省 值 就 可 以使用 Default 关键字
替代该参数 只要没有提供变量的全部参数都有一 个缺省值 还可以 在参数列 表末尾省 略
这些参 数变 量
注意 因为有 缺省值的参数 主要是 可选参 数 所以可把 它们放在参数 列表的 末尾 这
更便于 在调 用存 储过 程时 省略 相应 的变 量
调用存储过程 时指定参数的另外一种方法 是使用在存储过程定 义中定 义的相应名称
下面的 语句 说明 这种 技术
Execute GetCustDiscount
@CustId = 123789,
@Discount = @CustDiscount Output
使用参数 名称 正如本 例一样 不要求变量按 照任何 特定的顺序指定 下面的顺序 也
是有效 的
Execute GetCustDiscount
@Discount = @CustDiscount Output,
@Custid = 123789
Transact-SQL 允许在某 些位 置指 定一 些参 数(以第 一个 变量 开始) 然 后 是另 外一些用参
数名称指 定的其他变量 如果使用这 种参数名称技术 指定一 个变量 那么 也必须 在调用时
使用参 数名 称指 定任 何后面 的 变量
2.11.1 返回 结果 集
存储过 程为 每一 个执 行的 Select 语句 返回 一个 结果 集 这种 Select 语句 没 有 用作一 个
标量 子查 询(也就是说 一个值) 并且不对 选择列表中 的全部列赋 予变 量或者 参 数 所以
例如 下面 的存 储过 程返回 两 个结 果集
Create Procedure ListLowHighDiscCust
As
select
From Customer
Where Discount <.01 select
From Customer
Where Discount >.1
调用这个存储 过程的应用程序可以在返回 的结果集中处理这些行 在 默认情况下 查
询分析 器和 OSQL 实用程序 显示 每一 个返 回的 结果 集
2.11.2 状态 返回 值
存储过 程可 以使 用 Return 语句 结束 执行 并 且 为调用 者 设 置将要返 回的 状态 值 如下
所示
Create Procedure ListCustWithDiscount
@MinDiscount Dec(5,3) = 0.001
As
If (@MinDiscount >1.0) Return (1)
select
From Customer
Where discount >= @MinDiscount
Return (0)
返回值是可选 的 并且可以是任何整数表 达式 为了得到这种状态值 可 在 存储过 程
调用上 写一 个赋 值
Execute @Status = ListCustWithDiscount .1
注意 SQL Server 使用 0 表 示成功 调用 -1 到-99 为系 统调 用错 误( 例如 数 据转换 错
误) 也可 以使 用 1 表示 成 功调用 但是 避免 返回 的状态 值 在-1 到-99 范围内
2. 12 存储过程 编程 技 巧
可以在存储过 程中的任何地方增加注释 可以是在两个短横之 后 直 到当前行末尾
或者分 散到 多行 的/ 和/ 分隔符之 间
--This is a comment
/ And so is this
multiline comment/
2.12.1 无限 定的 对象 名 称
在存储过程中 无限定的对象名称暗示由 存储过程所有者名称限定 对于那些可能由
其他用 户拥 有的 对象 一 定要明 确地 限定
2.12.2 本地 变量 声明
所有在存储过 程中的本地变 量都必 须在使 用之前声明 并且必须在存 储过程中有唯一
的名称 变量 的适 用范 围 是从其 声明 开始 到 Create procedure 语句 的 结 束的所 有代 码
注意 虽然 在存 储过 程中可 以 使用 Begin 和 End 语句 来创建 嵌套 的块 但是 这对变 量
的范围 没有 影响
变量声 明的 格式 类似 于参数 的 形式
Declare @variable-name datatype变量名 称的 语法 规则 与参数相 同 并且 允许使 用除 了 Text NText 和 Image 外的数据
类型 不能 为变 量声 明一个 初 始值 下面 是一 个声 明 整数变 量的 示例
Declare @RowCnt int
2.12.3 赋值 语句
可以使 用 Set 语句 对一 个变 量或 者参 数赋 值
Set @RowCnt = 1
虽然可以在存 储过程中为输入参数和输出 参数赋值 但是改变一个输 入参数的值对调
用者的 数据 没有 影响
还可以 对在 单行 Select 语 句的选 项表 中的 全部 元素 增 加一个 赋值 表达 式 如 下所示
Create Procedure GetCustnameAndDiscount
@CustId Int,
@Name VarChar(30) Output,
@Discount Dec(5,3) Output
As
select @Name =Name,
@Discount = Discount
From Customer
Where CustId = @CustId
这种多行赋值 的形式可以避免几乎相等查 询的多 次执行 从 而 提高性 能 然而 一定
要小心 这只 能用 于那 些 保证 只返回 一行 的查 询
2.12.4 语句 块
无论在 存储 过程 的任 何地方允许使 用 一 个单 独的 SQL 语句 都 可 以使用语 句块 语句
块由 Begin 和 End 语 句分隔 开 所以 例如 下面 的 三个语 句块 将作 为一 个复 合 语句对 待
Begin
Set @DeletedRowCnt = 0
Delete From Customer
Where CustId = @CustId
Set @DeletedRowCnt = @@RowCount
end
对于编 码 If…Else 语句 和 为编码 While 循环体 语 句 块是非 常重 要的
2.12.5 显示 消息
Print 语 句允许返 回任何字符 串表达 式 包括 文字 字 符参 数 和变量 以及传 给调 用者
消息句 柄的 函数
Print Cast(@CustCount As VarChar(10)) + `rows''
将要返 回的 字符 串最 多可以有 1024 个字 符
注意 在默 认的 情况 下 查询分 析器 和 OSQL 消息 句柄 显示 由 Print 语 句返回 的 任何消
息 ODBC API 在 诊断 记 录中返回消 息可 以由 应用 程 序检 索的文本2.12.6 条件 执行
可以在 存储 过程 中使 用 If…Else 语句 其方法 类似 于 在 HLL 程序中 使用 这种控制结构
If 关键字后面 是一 个条 件 该条件 可 判断是 真 假或 者未 知 就像 在本 章前 面“条 件 和子查
询”一节 中描 述的 那样 当条 件为 真时 执 行该 条件后 面的 语句 当该 条件 为假 或者 未知 时
执行在 Else 关键 字 如果指 定 后面的 语句 下面 是一 个有 趣的 示例 强调 写 存储过 程时
考虑空 值的 重要 性
Create Procedure TestNull As
Declare @x int
Set @x = Null
If ((@x =0) or (@x <>0))
Print `True''
Else
Print `Unknown''
由于 ANSI 标准 行为 该存 储过 程 将打印 Unknown 这是由于 SQL 的三 值 逻辑规
则 (回忆 一下 任 何与 空 值比较 的结 果都 是未 知 两 个未知 值的“ 或”结 果还是 未知 )
可以嵌 套 If…Else 语句 控 制结构 并 可以在一 行 上编写多个 SQL 语句 所以表 达多 步
条件的 清晰 方式 是使 用 Else If 结构 如下 所示
If ( @CustCount =0) Begin
Print `No rows''
End
Else If (@CustCount=1) Begin
Print `1 row''
End
Else Begin
Print Cast (@CustCount As VarChar(10)) +`rows''
End
几次编 程实 践可 以避 免这种 问 题 并且 使代 码易 于阅读 使用 括号 括住 If 语 句 上的条
件 并且 使用 嵌套 的括 号 明确地 说明 复合 布尔 表达 式 的判断 顺序 另外 对 于一个 If 或者
Else 分支 中的 所有 代码 使用 Begin 和 End 块 即使 是在 该块 中只 有一 条语 句也是如 此 如
果没有 一直 使用 Begin…End 块 那 么很 容易 编码 一 些看起 来像 多条 语句 的 Else 分支 但
是 事实 上不 是 如 下示例
If (@Custcount =0)
Print `No rows''
Else
Delete From Customer Where Discount Is Null
Print Cast (@CustCount As VarChar(10)) +`rows''
如果没 有为 Else 分支 定义一 个块 那么 最后 一条 语句 总是 要执 行 因为 它不 是 If…Else
语句的 组成 部分
2.12.7 While 循环
可以在 存储 过程 中使 用 While 语句 定义 一个 执行 循环 在 While 关 键字之 后 编码 一
个控制循环执行的条件 在 While 语句体的每一次执行之前( 简 单语句 或者 块语 句) 都要测试条 件 如果条件 为 真 执行 循环 体 如果 条件 为 假或者 未 知 控 制 流程转到 While 语
句后面 的语 句 下面 是一个 While 循环的 简单 示例
Create Procedure TestWhile As
Declare @Count int
Declare @Limit int
Set @Count =0
Set @Limit=10
While (@Count<@Limit) Begin
Print Cast (@Count As VarChar(10))+`iteration''
Set @Count=@Count+1
End
在 While 循环 内 可 以使用 Continue 和 Break 语句 修 改执行 流程 Continue 条件将控
制移到 While 循环 的开 始(条件测 试) Break 语句 立即 退出 循环
2.12.8 GoTo 语句 和标 签
SQL 有一个 GoTo 语 句和语 句标 签 但 不 推荐使 用 GoTo 编 程技术 为了 编码 一个 标
签 在标 识符 后面 加一 个 冒号 在 GoTo 语句上写 的 标签没 有冒 号 如下 所示
SkipNextStep:

GoTo SkipNextStep
2.12.9 WaitFor 语句
为了 暂停执行 一个指定 的 时 间间 隔 或 者到 一 个 指定 的时间 可以 使用 WaitFor 语句
该语句 有两 种形 式 如下所 示
WaitFor Delay `00:10:30''
或者
WaitFor Time `14:30''
第一个示例使用 Delay 关键 字指定时间间隔 为 10 分钟 30 秒 可以 使用任何有效 的
datetime 格式 但没 有日 期部 分 所以最 长延 迟时 间是 24 小时 第 二个示例使 用 Time 关
键字等 到下 午 2:30
2. 13 游 标
SQL 游标 提 供 了 一种循环 结 果 集 分别读 每一行的方式 在 存 储过程中 游标 提供 了
一种有 用 的 技术 它可 以 实现不 易用 Select 语句 语 法轻 易地表 达的 复 杂 计 算 可 修改的 游
标还提供 了一种手段 用于在结果集 中根据复杂的条 件选择 修改或者删除行 为 了 使用 一
个游标 有五 个基 本步 骤
1). 声明 游标
2). 打开 游标
3). 从游 标中 重复 提取 读取 行 有选 择地 修改 或者 删除 所取 的行
4). 关闭 游标
5). 当不 再需 要游 标时 释 放游标下面是 一个 使用 游标 的示例 列出 客户 ID 和名称
Create Procedure ListCust As
Declare @CustId Int
Declare @CustName VarChar(30)
Declare CustCursor Cursor For
Select CustId,Name
From Customer
Order By CustId
For Read Only
Open CustCursor
While (0=0) Begin
Fetch Next
From CustCursor
Into @CustId,@CustName
If (@@Fetch_Status <> 0) Break
Print Cast (@CustId As VarChar(10)) + +@CustName
End
Close CustCursor
Deallocate CustCursor
游标名 称在 Declare 和 Cursor 关键字 之间 并且 是在以 后 的语 句中 用于引用该游标 的
名称 游标 声明 的核 心是一 个 Select 语句 它 可以 包 括前面 讨论 的除 了 Compute 子句以 外
的所有 子句 特别 是 Select 语句 可以 用 Order By 子句排列 所取 出的 行的 顺序 重要 的是
Select 语句 还可 以在 Where 子句中 使用 参数 和变 量(以 及允许标 量值 的 其他 位置) 因此 可
以定义 一个 游标 以便 结 果集依 赖执 行时 间值 由 Select 语 句定义的 结果 集在 执 行 Declare
Cursor 语句 时并 不实 际生 成 而是 当对 该游 标执 行 Open 语句 时 生成该结果集 这样就
允许在打 开 或重新打 开 游标 确定取出包括在结 果集中 的那些行之前 可 以 改变用 于
游标的 Where 子 句的任 何变量值
第一个 Fetch Next 语句 阅读 第一 个选 择的 行 并且 把选择列表中的 每一个值 放到 在 Into
子句的 相应 位置 指定 的变量 或 者参 数中 后面 的 Fetch Next 语 句阅读下 一行 直到 读完 结
果集中 的全 部行 为止
注意 虽然 Into 子句 是可 选的 但是 Fetch 不 能 将值 检 索到变 量或 者参 数中 因而 没
有实际 用途
在 Fetch 之后 SQL Server 设置@@Fetch_Status 内置函数 返 回 0( 成功) -1( 没有未 读
的行)或者-2( 行在 Fetch 操作之 前已 经被 删除) 可以测 试@@Fetch_Status 来 控制循环 如
本示例 所示 应留 意@@Fetch_Status 为由 连接 使用 的任 何游 标返回 最 近的 Fetch 的状态
所以如 果需 要 在 过程 中的 后 面 使用 该 状 态值 那 么 应该 把@@Fetch_Status 状 态 值赋一个 变

一旦完成了提 取行 就应该关闭游标 可 以重新打开一个游标 重新 从开始处理同样
的结果集 或者处理一个 不同的结果集 这取决于在游 标声明 中使用的本地 变量的 新值 当
不再需 要一 个游 标时 应 该释放 该 游标来 释放 系统 资 源2.13.1 可滚 动的 游标
在默认 情况 下 Fetch 操 作 的 唯一类型 是 Fetch Next 对 于 更加灵活的操 作 可以 在 Cursor
关键字 之前 增加 Scroll 关键 字
Declare CustCursor Scroll Cursor For
select CustId,Name
From Customer
Order By CustId
For Read Only
可滚动 的游 标允 许各 种 Fetch 操作 使用 如表 2-6 所 示的关 键字
表 2-6 Fetch 语句的定位关键字
关键字 定位游标 并读取行
Next 当前行的下一行
Prior 当前行的前一行
First 第一行
Last 最后一行
Absolute n n>0 定位到从开始的第 n 行
n=0 没有返回的行
n<0, 定位到末尾前的第 n 行
Relative n n<-1 定位到当前行之前的第 n 行
n=-1 同 Prior 关键字相同
n=0 定位到当前行( 重读)
n=1 同 Next 关键字相同
N>1 定位到当前行之后的第 n 行
2.13.2 不敏 感的 游标
在默认情况下 大多数游标是动态的 也就是说 当其他进程修改包 括在游标结果集
中的 行时 后面的 Fetch 语句将得到新值 如果 希望 行集 固定在打 开游标的 时刻 在游标
名称后 面增 加 Insensitive 关键字 这将使 SQL Server 制作 结果 集中 行的 临时 拷贝 并且 把
这个临 时拷 贝中 的行 返回到 后 面的 Fetch 语句 即使 没有 Insensitive 关键字 当 某个游 标的
Select 语句 有 Distinct Union Group By 或者 Having 关键 字时 SQL Server 对 待 该游标 好
像指定 Insensitive 关键字一 样
2.13.3 可修 改的 游标
如果希 望修 改或 者删 除通过 游 标取 出的 行 那么 可以用 For Update 子 句而不 是 For Read
Only 子句声明 该游 标 (不 能 在 不敏感的游 标上 使用 For Update 子句 如上所 述) For Update
子句还 允许 一个 可选 的列项 这是 限制 Update 语句 的 Set 子 句为列出 的 列的结果 下面
的示例 说明 一个 可修 改游标 一个 取样 Fetch 与只 读游 标上 使用 的 相同 以 及 在当前行上
Update 和 Delete 语句 的形 式
Declare @CustId Int
Declare @Discount Dec(5,3)Declare CustCursor Cursor For
Select CustId,Discount
From Customer
Order By CustId
For Update of Discount
Open CustCursor

Fetch Next
From CustCursor
Into @CustId,@Discount
If (@@Fetch_Status = 0) Begin
If (@Discount=0.0) Begin
Delete From Customer
Where Current of CustCursor
End
Else If (@Discount>.5) Begin
Update Customer
Set Discount =.5
Where Current of CustCursor
End
End

Close CustCursor
Deallocate CustCursor
注意 两个 Update 和 Delete 语句 的 Where 子句 如何 使用 Current of cursor-name 短语
指定将 要在 基表 中修 改或者 删 除的 行 这两 种语 句称为定位修改和定 位删 除
2.13.4 其它 游标 选项
SQL Server 有 Declare Cursor 语句的 扩展 形式 提供了其它选项 如表 2-7 所示 为了
使用这 些选 项 不能 在 Cursor 关键 字之 前使 用 Insensitive 或者 Scroll 关键 字 相反 可以
在 Cursor 关键 字之 后 编码 一个 或者 多个 在表 2-7 第一 列 中 的关键 字 如果 编码多 个 关键
字 按照 表 2-7 中行 的顺序 编 码
使用 Declare Cursor 语句 的扩 展形 式 还可 以编 码一个 For Update 子句 限制要修改
的列 不能 使用 For Read Only 子句 因为 该选 项由 Read_Only 关键字提供 了
注意 Declare Cursor 语 句 的非扩 展形 式是 ANSI 标准形 式 除非 需要 一个 只能由 扩 展
形式提 供的 选项 否则 建 议使用非护 展形 式
表 2-7 扩展的游标选项
扩展的游标关键字 效果
Local 指定游标范围是定义游标的存储过程 Global 指定游标的范围
Local
是本次连接 并且可以由任何存储过程使用 当这两个选项都没有指
Global
定时 缺省值是 Global 除非已经使用 sp_dboption 系统存 储过 程 数
据库的选项“default to local cursor option” 设置为打开 ( 建议使用本地
游标 避免在多个过程中无意中访问同一个游标而引起的边缘效
应 ) 续表
扩展的游标关键字 效果
Forward_Only 指定唯一可用的 Fetch 选项是 Fetch Next Scroll 指定可
Forward_Only Scroll
以使用列在表 2-6 中的 Fetch 选项 如果指定 Dynamic Static 或者
Keyset 选项 那么缺省值是 Scroll, 否则是 Forward_Only
Dynamic 指定结果集是动态的 如上所述 FastForward 指定游标是
Dynamic FastForward
Forward_Only 和 Read_Only 并且有性能优化 Static 指定游标是不
Static Keyset
敏感的 如上所述 Keyset 指定行集 和它们在结果集中的顺序当游 标
打开时不会改变 然而 当提取到时任何对非主键列的变化都可以反
映出来
Read_Only 等价于前面讨论的 For Read_Only 子句 Scroll_Locks 指定
Read_Only Scroll_Locks
游标是可修改的 并且行锁保持在取出的每一行上 因此保证该行可
Optimistic
以修改或
者删 除 Optimistic 指定游标是可修改的 但是行锁不保持在取出的
每一行上 相反 SQL Server 基于自从该行取出后 另一个进程是否
修改该行 来确定是否允许修改
如果游标隐含地从一种类型转变到另一种类型 指定省略警告消息
Type_Warning
2.13.5 游标 参数 和变 量
除了标量参数 和本地变量之外 如前所述 还可以声明游标的输出参 数和变量 如下
所示
Crate Procedure OpenGoodCust
@GoodCustCursor Cursor Varying Output,
@MinDiscount Dec(5,3)=.2
As
Set @GoodCustCursor=Cursor For
Select Custid,Name
From Customer
Where Discount> =@MinDiscount
Order By CustId
For Read Only
Open @GoodCustCursor
为了声 明一 个游 标 参 数 在 参 数名称后 面加 上 Cursor Varying Output( 这三 个 关键字都
要求) 然后 使用 Set 语 句设置参 数( 或游 标 变量) 如 这 个示例所 示 可以使用 下 面 的语
句调用 这个 过程 并使 用返回 的 游标
Declare @CustCursor Cursor
Declare @CustId Int
Declare @CustName VarChar(30)
Execute OpenGoodCust @CustCursor Output, .3
While (0=0) Begin
Fetch Next
From @CustCursor Into @CustId, @CustName
If (@@Fetch_ Status <> 0) Break
Print Cast(@CustId As VarChar(10))+ + @CustName
End
Close @CustCursor
Deallocate @CustCursor
注意 这个游 标只能传回到调用程序 不 能做为一个参数提供给过程 调用 游标参数
的一种用 法是在被调用 的过程中使用 逻辑来 选择要打 开和传 回的游标 因此简化 调用过 程
的进程
2. 14 存储过程 错误 处 理
作为一 个老 生常 谈的 话题 本节 讨论 如何 进行 存储 过 程的错 误处 理
2.14.1 调用 RaisError 语句
如果存储过程碰到一个问题 希望向调用程序发送一个错误消息 那么可以使用
RaisError 语句( 注意 不 要 拼错这 个关 键字) 下面是一个示 例 用折扣值的替换参数发 送
一个 ad hoc 消息
Create Procedure ListCustWithStatus
@Starus VarChar(10)
As
If (( @Status < > ''Active'') And
( @Status < > ''Inactive'') And
( @Status < > ''Deleted'')Begin
RaisError(''Invalid status: %s. '' , 16, 1,@Status)
Rerurn
End
Select
From Customer
Where Status= @Status
发送 ad hoc 消息 的 RaisError 语句 的基 本语 法是
RaisError(message,severity,state,substitution1,…
消息最 多可 以有 8000 个字 符 最多 可以 包括 20 个替 换参数 每一 个由 百分 号和一 种
格式说 明表 示 格式 说明可 以 是%s 表示字 符串 %d 表 示整数 不支 持浮 点数 和 小数替 换
参数 格式 说明 的附 加信息 参见 Transact-SQL 说明书的 RaisError 语句
严重程 度值 是 0 到 25 之间 的整 数值 值 20 到 25 终 止当前的连 接 状态 值是 1 到 127
之间的 一个 整数 并且 有 应用程 序指 定的 含义
对于字符串中 的每一个替换参数 必须提 供一个文字 参数或者变量 这个值将与发
送到调 用程 序的 消息 串合并
2.14.2 调用 sp_addmessage 系统存 储过 程
使用 sp_addmessage 系统 存储 过程 调用 可 以 不用消 息串 而 用 一个消息 ID( 在 50001
到 2147483647 之间 的一 个整数)来确认 一个 已经 增加 到 sysmessages 表中的消 息串
sp_addmessage 60001,16, ''Invalid starus: %s.''
在这个 示例 中 第一 个参数 是 消息 ID 第二 个是 严重 程度 第三 个是消息串 下面是
一个如 何使 用这 种预 定义消 息 的示 例
RaisError(60001, 16, 1,@Status)
为了替 代一 个 sysmessages 表中已 有的 消息 使用 如 下的语 句
sp_addmessage 60001, 16, ''Invalid starus: %S.''Null,False,Replace
为了删 除一 个 sysmessages 表中已 有的 消息 使用 如 下的语 句
sp_dropmessage 60001
在默认 情况 下 @@Error 内置 函数 为严 重程 度低 于 10( 包括 10)的 消息设置 为 0 为严
重超过 10 的消 息设 置错 误号 Ad hoc 消息 的隐 含消 息 ID 为 50000 可 以增加 With SetError
选项来 指定@@Error 设置 为消 息 ID 而不 考虑 消 息 的严重 程度
RaisError(60001,16, starus)With SetError
可以在 With 子句 上指 定的其 他 选项 是
Log 在服 务器 的错 误日 志 中和事 件日 志中 登 录错 误 对于 严重 程度 超过 18 的消
息要使 用求 该选 项
Nowait 立即 发送 消息 给 客户
调用另 一个 发 送 错误 消息 的 存 储过 程的 一 个存 储 过 程可 以 通过测 试@@Error 函数 检
查最近 的错 误
if (@@Error>0)Begin
…handle error
End
当测试@@Error 时要 小心 因为它在 每个操作 中重新设 置 下面 的 代 码提 供 了一 种保
护和测 试@@Error 值的 安全 方式
Declare @ProcError int

Execute MyProc
Set @ProcError=@@Error

If(@ProcError>0)Begin
… handle error
End
2. 15 触 发 器
触发器 是一 种特 殊的 存储过 程 当 Insert Update 或者 Delete 语 句修改 表中 一个 或者
多个行 时 执行 触发 器 因为 SQL Server 对特 定表 上的 每一 个指 定操 作调用 一个 触发 器
所以可 以使 用触 发器 扩展 SQL Sever 的内 置完 整性 和数 据操 纵功 能
注意 不像 Delete 语句 Trancate Table 语句 不激 活触 发器 Write Text 语 句也不 激活
触发器
在 SQL Sever 2000 中支 持两 种类型 的 触发 器 前触 发器(Instead Of Trigger)和 后 触发器
(After Trigger) 前 触发 器就是在 语句 执行之前 激活 触 发器 而后 触发 器就是在 语 句执行 之后激活 触发 器 可以 通过 FOR 子句来 选择 使用 何种 触发 器 下面 首先 以 SQL Sever 2000
默认的 后触 发器 为例 介 绍触发 器的 使用 方法
通过使 用如 下的 Greate Trigger 语句 创建 一个 触发 器
create Trigger TrackCustomerUpdates
On AppDta.dbo.Customer
For Update
As
Insert Into AppDta.dbo.CustUpdLog
(CustId,
Action,
UpdUser,
UpdDateTime)
Select CustId,
‘Update’,
Current_User,
Current_TimeStamp
From inserted
这个示 例说 明在 触发 器定义 中 的关 键元 素 触发 器名称 在 Create Trigger 关键 字之 后
On 子 句指定一 个 基 表 该 触发 器 与此表相 关联 ( 不能在视图上 创 建 后 触发器) For 子句
指定触 发器 激活 的动 作 可以使 用一 个或 者多 个 Insert Update 或者 Delete 关键字 然后
是 As 关键 字, 可以 编码 一个 前面 讲过 的存 储过 程 在这个 示例 中 触发 器由 一个 Insert 语
句构成 它向 一个 跟踪 对 Customer 表修 改的 表中 增加行
一旦创 建了 这个 触发 器 对每一 个在 Customer 表上执行 的 Update 语句 SQL Server
自动 激活 触发 器的 存储 过 程 在 触发 器的存储 过程 中 可以 使用 inserted 标识符引用一个
临时的 驻留 内存 的表 它包 括 了受影 响的表 的 全 部新行
注意 不需 要在 触发 器中创 建 一个 inserted 表 SQL Server 管理这些由触发器自 动使
用的驻 留内 存表
在这个 示例 中 从 Customer 表的更 新行 中的 CustId 值是 插入 到用 户定 义的 CustUpdLog
表的新 行的 一部 分 在 CustUpdLog 表中 的另 外三个列 保 存动 作类 型( 例如 Update) 执行
该动作 的用 户和 时戳 为 了也跟 踪 Insert 和 Delete 操作 除 了前面那 个语 句 外 还可以使
用下面 两个 Create Trigger 语句
Create Trigger TrackCustomerInserts
On AppDta.dbo.Customer
Instead Of Insert
As
Insert Into AppDta.dbo.CustUpdLog
( CustId,
Action,
UpdUser,
UpdDateTime)
Select CustId,
''Insert''
Current_User, Current_TimeStamp
From inserted
Create Trigger TrackCustomerDeletes
On AppDta.dbo.Customer
For Delete
As
Insert Into AppDta.dbo.CustUpdLog
( Custid,
Action,
UpdUser,
UpdDateTime)
Select CustId,
''Delete''
Current_User,
Current_TimeStamp
From deleted
第二个 触发 器使 用 deleted 标识符 它引 用一 个临 时的驻 留 内存 表 该临 时表 包含 触发
器影响 到的 表的 全部 旧行
注意 由 inserted 和 delete 关键 字 引用 的临时 表与 定 义触发 器的 基表 有同 样的列 结 构
当为每一种操 作创建一个触发器时 可以 为所 有 三种 操 作创建 一 个触 发器 并且使用
相应的编程技术处理 每 一种操 作 下面的示例在 For 子句中列出了三种语句类型 并且使
用条件 语句 将相 应的 跟踪值 插 入到 CustUpdLog 表中
Create Trigger TrackCustomerUpdates
On AppDta.dbo.Customer
For Insert,Update,Delete
As
Declare @InsertedCount Int
Declare @DeletedCount Int
Set @InsertedCount=(Select Count()From inserted)
Set @DeletedCount=(Select Count()From deleted)
If ( @InsertedCount>0)Begin
Insert Into AppDta.dbo.CustUpdLog
( CustID,
Action,
UpdUser,
UpdDateTime)
Select CustId,
Case
When( @DeletedCount>0)Then
''Update''
Else ''Insert''
End,
Current_User,
Current_TimeStamp From inserted
End
Else If(@DeletedCount>0)Begin
Insert Into AppDta.dbo.CustUpdLog
( CustId,
Action,
UpdUser,
UpdDateTime)
select CustId,
''Delete'',
Current_User,
Current_TimeStamp
From deleted
End
正如本 例所 示 无论 何时 Insert 或者 Update 语句 影响 一个 或者 多行 时 inserted 临时
表都有 记录 行 无论 何时 Delete 或者 Update 语句 影 响一个 或者 多行 时 deleted 临时表都
有记录 行 对于 一个 Update 语句 deleted 临时 表有 旧行 inserted 临 时表有新 行 这个 示
例还反 映了 触发 器的 另一个 重 要方 面 对于 某个 表 的 Update 或者 Delete 操作 即使该语
句没有 影响 到行 也激 活 触发器 ( 也就 是说 没有 满足 Where 子 句的行) 触发器的 存储 过
程应该 预测 这种 可能 性
2.15.1 检查 指定 列的 变 化
对于 Insert 或者 Update 触发 器 可以 在 As 关键 字后 使用 一个 If Update(colunm-name)
子句 测试 该 Insert 或者 Update 语句 是否 明确 地列 出一 个或 者多 个特 殊的 列 下面的 示例
说明如 何使 用这 种测 试
Create Trigger TrackDiscountUpdates
On AppDta.dbo.Customer
For Update
As
If Update(Discount)
Insert Into AppDta.dbo.DiscountLog
( CustId,
Action,
Discount,
UpdUser,
UpdDateTime)
select CustId,
''Update'',
Discount,
Current_User,
Current_TimeStamp
From inserted
可以在 If 子句 中使 用 And 和 Or 逻辑 运算 符来 合并 测 试 如下 示 例
Create Trigger TrackCustomerUpdates On AppDta.dbo.Customer
For Update
As
If Update(Name) And Update(Discount)…
Create Trigger TrackCustomerUpdates
On AppDta.dbo.Customer
For Update
As
If Update(Name)Or Update(Discount)…
对于 Update 语句 如果 该 列列在 Set 表达 式中 那么 Update(colunm-name) 测试为真
SQLServer 不检查新 值是否 与 旧值 不同 对于 Insert 语句 如果 该 Insert 语句没有 列 项( 意
思是全部列) 或者如果该列明确地列在列项中 那么 Update(column-name) 测试为真
Update(column-name) 测试的规则 可以 应用 到是 否使 用文 字 表达 式或 者 NULL 或者 Default
关键字 赋一 个新 值或 者改变 值
对指定 列的 另外 一种 测试是 使 用 Colamn_Updated 函数 它返 回一 个位 图 表示在 SQL
Insert 或者 Update 语句 中指 定哪 些列 表 中的第 一列 由位 图的 最低 顺序 位置 表示 第二 列
由位图 中较 高位 置表 示 等等 下面 的语 句测 试是 否 指定第 一 第三 或者 第四 列
Create Trigger TrackCustomerUpdates
On AppDta.dbo.Customer
For Update
As
If(Colunms_Updated()&13)>0…
在这个 示例 中 标记 值 13 是代 表 1 4 和 8 列的三个 位图值 之和 可以 使 用 任何按位
操作来 标号 Columns_Updated 函数值 然 后 使用 逻辑 比 较运算来 测 试 这 个标号值 我们 建
议使用这种位图技术 因 为 它 依赖于表中 列的 相对 位 置 Update(column_name) 测试是一种
较好的 技术 因为 它不 依 赖 于列的位 置
2.15.2 修改 和删 除触 发 器
为了改变一个触发器的 定义 可 以删 除和重新创建该 触发器 或者使用 Alter Trigger
语句 为了 删除 一个 触发器 使用 Drop Trigger 语句 如下 所示
Drop Trigger TrackCustomerUpdates
除了第 一个 关键 字之 外 Alter Trigger 语句有 与 Create Trigger 语 句相同的 结构
2.15.3 使用 触发 器
不仅 可以为一 个表创建 多 个 触发 器 而且 还可以为 一 个 表的 同一个 SQL 语句( 例如
Update 语句) 创建 多个 后触发 器 不能 为同 一个 SQL 语 句 创 建多个 前触 发器 每 一个新的
Create Trigger 语句 增加 触发 器到 那些 指定 表和 语句已有 的 触 发器中 对 于所创建 的多 个触
发器 可以用系 统存储过程 sp_settriggerorder 来指定第一个 被激活的触发 器和最后一个被
激活的触 发器 而对于 其他的触发器 则不能指定其激 活顺序 只能由系统 决定 这种触发
器的特征 不会引起任何 特殊的问题 因为总是可以实 现各种 动作作为正常 的存储 过程 并
且按照 要求 的顺 序从 一个触 发 器中 调用它们
注意 虽在 7.0 及以 前的版本 中是不能 指定触发 器的激活 的顺序的 包括第 一个和最
后一个
触发器总是创 建在与其相关的表所在的同 一个数据库中 并且 在该 数 据库中 每一个
触发器 必须 有唯 一的 名称 可以 使用 所有 者名 称限 定 触发器 名称 例如 dbo.Track Customer-
Updates 它应 该与 表的 所 有者相 同
注意 虽 然一个触发器 总是与同 一个数据库中 的一个 基表相关联 但 是触发器可以 引
用 其 他数 据库 中的 对象 这种功能提供了一种方法 在不同数据库的两个表之
间实现 参考 完整 性而 Create Table 外键支 持不 允许 这 么做
尽管 触发 器是 一 种 存储 过 程 但是不 能使 用 Execute 语 句调用 它 如 果 有希 望共 享 触
发器和 正常 的存 储过 程的编 码 那么 只 需 把共享代 码 放在存 储过 程中 从触 发器中 调 用它
如果一个触发 器修改一个表 那么这些修 改可能会激活另一个触发器 或者本身 在默
认情况 下 SQL Sever 允许这种 嵌套 的触 发器 调用 深度 为 32 层 虽然我 们建 议 允许嵌 套的
和叠代的 触发器 但是 可以使用系统 存储过程禁止这 么做 下面 的 语 句 在 指定 的 数 据 库 上
防止叠 代触 发器
sp_dboption AppDta,`recursive triggers'',`false''
为了在 所有 数据 库中 防止嵌 套 触发 器调 用(包括 叠代 调 用) 可以使用下 面的 语 句
sp_configure `nested triggers'',0
2.15.4 显示 有关 触发 器 的信息
有三个 系统 存储 过程 可 以显示 有关 触发 器的 信息
sp_help trigger_name 显示 触发 器的 所有 者和 创建 时 间
sp_helptext trigger-name 显 示触发 器的 源代 码
sp_depends trigger-name 显示 该触 发器 参考 的对 象清单
如果丢 失了 用来 创建 触发器 的 源代 码 那么 sp_helptext 系 统存储过 程非 常有 用
2.15.5 前触 发器
前面以 后触 发 器 为例 介绍 了 触 发器 的 基 本内 容 下 面再 介 绍一下前 触发 器 的 不同之 处
要创建 一个 前触 发器 必须 用 Instead Of 显式 声明 如下面 的例 子
create Trigger TrackCustomerUpdates
On AppDta.dbo.Customer
Instead Of Update
As
Insert Into AppDta.dbo.CustUpdLog
(CustId,
Action,
UpdUser,
UpdDateTime)
Select CustId,
‘Update’,
Current_User,
Current_TimeStamp
From inserted与后触发器不 同的 是 前 触发器既可 以在 表又可以在视图上 创建 但一条语句 只能创
建一个 前触 发器 因此 前 触发器 不存 在激 活顺 序问 题
2. 16 触发器编 程技巧
一般地 触 发器 编程 类似于 存储过 程的 编程 然而 对 于触发 器编 程 还要 考虑几点
例如 不能 在触 发器 中使用 下 面这 些语 句
Alter Database Create Trigger Drop View
Alter Procedure Create View Grant
Alter Table Deny Load Database
Alter Trigger Disk Init Load Log
Alter View Disk Resize Reconfigure
Create Database Drop Database Restore Database
Create Default Drop Default Restore Log
Create Index Drop Index Revoke
Create Procedure Drop Procedure Truncate Table
Create Rule Drop Rule Update Statistics
Create Schema Drop Table
Create Table Drop Trigger
2.16.1. 改变 触发 程序 语 句
现在用 户能 改变 触发 程序对 象 而不 必从 数据 库和 syscommands 系统 表中 删除该 对 象
这是非常 重要的 因为 触发程序携带 了在删除对象时 被销毁 的其他引用和 许可 改变该 对
象而不 是删 除它 可以 保 留与该 对象 相关 的引 用和 许 可 如果 使用 WITH APPEND 选项
则对每 个 Update Insert 和 Delete 而言 可存 在多 个 触发程 序 这意 味着 对每 个 Update Insert
和 Delete 而言 可激 活多 个触 发程 序 WITH APPEND 的 功能存在 于由 sp_dbcmptlevel 设
置的 7 级兼 容性
Alter Trigger 的用 法
Table Owner
Data Definition Language Administrators
DBO
其语法 如下
ALTER TRIGGER trigger_name
ON table
WITH ENCRYPTION
{FOR { , INSERT , UPDATE , DELETE }
NOT FOR REPLICATION
ASsql_statement ...n
}
{FOR { , INSERT , UPDATE }
NOT FOR REPLICATION
AS
IF UPDATE (column)
{AND | OR} UPDATE (column) ,...n
sql_statement ...n
}
这是如 何用 Alter Trigger 语句 改变 插入 触发 程序 的实 例
ALTER TRIGGER mytrigger
ON tablename
FOR INSERT
AS RAISERROR ( mytrigger error , 1, 2)
下表描 述了 Alter Trigger 的自 变量
Alter Trigger 参数 描述
要改变的触发程序名
TriggerName
触发程序将执行的表名
TableNa me
在 syscomments 系统表中加密编码关键词
WITH ENCRYPTION
{ , INSERT , 表示哪个语句将激活该触发程序
UPDATE , DELETE
, INSERT ,
UPDATE } 如果与复制相关的 sqlrepl 注册 ID 修 改了表格 则不能激活
NOT FOR REPLICATION
该触发程序
下一个是 Transact-SQL 语句
AS
Transact-SQL 语句 是触发程序的 Transact-SQL 语句
根据对指定的列是 INSERT 还是 UPDATE 来提供 IF 逻辑
IF UPDATE(ColumnName)
检查 INSERT 或 UPDATE 操作的列名
ColumnName
2.16.2 使触 发程 序能 递 归调用 它本 身
sp_dboption
如果设 置了 递 归触发 程序 选项 并且 更新 了触 发程 序内 部的 表格 该触 发
程序就 可以 递归 调用 自身
下面是 一个 实例
sp_dboption mydb, recursive triggers , true
在这个 实例 中 如 果更 新 了触发 程序 内部 的表 就把 recursive trigger 数据库 选项设 置
为真 而且 允许 该触 发程序 递 归调 用其 自身
2.16.3 多个 触发 程序 对 应一个 触发 动作
如果指 明了 WITH APPEND 或把 sp_dbcmptlevel 的兼容性 等级 设置 为 70 就有多个 触
发程序 对应 于数 据库 修改操 作 Update Insert 和 Delete触发器不应该 将结果集返回给调用程序 这就意味着不能在触发器中 编码自由格式的
Select 语句 最好 使用 标量 子查 询为 变量 赋值 因为 这种语句不创建结果集 还可在触发
器的开 始编 码 Set NoCount ON 语句 防止 SQL Sever 发送消 息 说明在存 储 过程 中 每一 条
语句影 响了 多少 行
注意 如果 在触 发器 中(或 者在正 常的 存储 过程 中) 使用 Set 语句 那么 当触 发器 完成 执
行时 Set 选项 值恢 复到 其 原来的 值
触发器的其中 一个作用是对超出主键 外 键和检查约束范围的检查提 供附加的完整性
检查 在检 查了 触发 器中的 条 件之 后 可以 通过 一条 Rollback 语句 取 消 整个事 务 下面
的示例 说明 如何 编码 一个触 发 器 确保 为一 个已 经同意 提 供折 扣的 客户 赋于 一个折 扣
Create Trigger CheckDisckDiscountUpdates
On AppDta.dbo.Customer
For Insert,Update
As
--Check only if Discount column is explicitly set
If Update(Discount) Begin
Declare @User VarChar(256)
Set NoCount On
--Check only if an actual discount is being assigned
If (Exists ( select
From inserted
Where Discount >0)) Begin
--Make sure this user has approval to assign discounts
If ( Not Exists ( select
From Employee
Where UserName=Current_User And
DiscountAuthority = `Y'')) Begin
--Send message and abort the transaction
Set @User=Current_User
RaisError ( `User %s not approved to assign discounts.'',
16,1,@User)
Rollback
End
End
End
对于每 一个 Insert Update 或者 Delete 语句 触发 器 调用一 次 并且 只有 在这 条语 句
满足全部的 主 键 外键和检查 约束时 才激 活触 发器 在激 活触 发器 时 SQL 语句 的结 果
反映到 目标 数据 库表 中(和 驻留内 存的 inserted 和 deleted 表中) 但是还没有 提交
在调用触 发器之前 一条 语句必须满足所 有的约束 这意味着不能使用触发 修改数 据
库 来满 足某 个约 束 例如 如果 有 Sale 行匹 配一 个客 户的 ID 值 假 设 有一外键约束
防止从 Customer 表中 删除一 行 可以 考虑 为在 Customer 表上的 Delete 操作 创 建一个触发
器 以便 删除 一个 Customer 行 也 将 删除全 部依 赖的 Sale 行 因此避 免出 现与 外 键约束 冲
突的“ 孤”Sale 行 在 SQL Sever 7 中 这种 操作 不能 进行 因为 在调 用触 发器 之前 系统 检查
外键约 束 并且 当检 查约束 时 有 孤行 那么该触 发器不 能调 用
触发器可操纵 目标表 这就意味着可以对 行做附加的操作 这些行是 由激活触发器的
SQL 语句插 入或 者修 改的 如果 使用 这种 功能 一 定 要避免 或者 处理 叠代 的触 发器 调用
2. 17 小 结
就像使 用任 何语 言一 样 遵守一 定的 编码 经验 和约 定 是有益 的 以便 SQL 代 码 更易维
护 下面 列出 了一 些有 益 的建议
对于反 复使 用的 DML 语句
创 建 一个 包括 这些 语句 的 存 储过 程 并且 把它 们存储在一个 源文 件中 这个
源 文 件可 以从 查询 分析 器 中调 用 或者使用 OSQL 在批处 理中运 行
在 SQL 源代码的 开始 处 加上一 些注 释 描 述执 行的动 作
使用空 格 空行 和分 割线注 释 可以 提高 源代 码的 可 读性
在 Select 子句 下 缩位 From Where Group By 和 Having 子句
对齐每 一个 子句 的开 始和连 续处(例如 Select 列项 from 表项 搜索 条件)
在多行 语句 上 为了 提高可 读 性 对齐 列名 表达 式 搜 索 条件等等
在选择 列表 中使 用 As 子句 为表 达式 或者 函数 提供一 个 有含 义的 列名
用查询 分析 器输 入 Select 语句时 考虑使 用 SubString 标量 函数缩短字符字段 另
外 考虑 使用 Cast 标 量函数 只 返回 数字字段 中 有 意 义的部分 这些 技术 可以 使 结
果更容 易查 看
使用有 意义 的表 相关 名称(也就是 说 别名) 例如 CurCust, 而不 是 C1)
当指定 表达 式 函 数或 者 搜索条 件时 考虑 允许 空值的 列
一定要保 证 用在基本条 件中的子查询 或者替代表达式中的标量 值的子 查询 不
能在结 果表 中有 多个 行
在限定 的条 件中 一 定要正 确使 用 Any 或者 All 关键字
一般地 当指 定 Group By 子句时 在 选择 列 表 中包括全 部的 组合 列 以便 结果表
中的每 一行 都有 该组 的确认 信 息
在 Insert 语句中使 用明 确的 列项 弄 明白每 一 个新 值与 列的对应
一定要 在 Update 或者 Delete 语句 中包 括一 个 Where 子句 除非 打算 删除 或者 修
改表中 的全 部行
当修改主键 唯一性键或者外键列时 或者当 删除由 外键引 用的表行时 一定 要
考虑主 键约 束 唯 一性 键 约束和 外键 约束 的影 响
当执行多行检 索或者修改语句时 一定要考虑 由其它 进程可能产生的访问 冲突
考虑在 DML 语句上使 用表提 示 或者 使用 其他 SQL Server 功能来防 止冲 突
当需要 保证 要么 全部 执行要 么 不执 行的 多行事 务的 执行 时 使用 提交 控制 语句
Transact-SQL 的数据操 纵语 言(DML) 是访问 SQL Server 数据库的 重要工具 作为一位
开发人 员可 以在 ad hoc 模式 下使 用 DML 语句 和工 具 例如 查询 分析 器 但是 最重 要的是
在 SQL Server 数据库上 建立 的大 多数 应用 程序 要求 使用 DML 编程 在本 章中 介绍 的编 程
建议有 助于 维护 代码 和更好 地 执行 SQL第二部分 桌面 应用开 发
SQL Server 2000 作为 Microsoft .NET Enterpise Servers 的重要组成部分 是
以后台数据库的身份出现的 对于应用 SQL Server 数据库系统的用户 访问和
操作数据库通常是通过前端 客户端 来完成的 这就是通常所说的服务器/ 客
SQL Server
户机模式 那么 如何通过前端程序访问和操作 数据库呢 本部分
将详细讨论这个问题 如何通过 Access ODBC ADO 在 C 中嵌入 SQL 访问
SQL Server 数据库第3 章 SQL Server 与其他产品集成 概 述
近来 Microsoft 公司 一直 坚持 着产 品集成 的 策略 不同 产品 之间 的差 别越 来越 少 其界
限越来越 不明显 有时 甚至很难区分 它们 同样 在 这产品 集成成为一种 趋势的 时期产 生
并发展 起来 的 SQL Server 也没什 么不 同 SQL Server 最初就 是设 计成 与操 作系统 特别 是
已推出 的 Windows 2000 以及 其他 Microsoft 应用 程 序集成 使用 的 SQL Server 的操作 系
统集成 来源 于企 业管 理器 MMC 插件 它 的系 统服 务 以及对 诸如 集群 和失 效等 OS 特性的
支持 一般 说来 产 品集成 允 许 用 户或 编程 人员在他们 所 处的 环 境 中访 问 SQL Server 比
如 尽管 基于 MMC 的 企 业管理 器提 供了 一套 具有 一 致外观 和感 觉的 MMC 插件 上百万
Microsoft Access 用 户仍 然 对这种 界面 表示 困惑 通过在 SQL Server 中增 加其 他程 序和 服务
器能访 问的 入口 Microsoft 已使 SQL Server 成 为 更 多人使 用的 可移 植的 工具
本章中 主要 介绍 SQL Server 与一些 Microsoft 产 品的集 成 包括 Microsoft Access 和其
他 Office 应用 产品 以 及 在服务 器端 或客 户端 与 Internet Information Services IIS 或 Internet
Explorer 的 Web 集成 编 程方面 SQL Server 也可以与 所 有 Microsoft 的 可视化 编程工 具集
成 如 Visual Basic Visual C++ Visual InterDev 等 在后 续的 章节 中我 们将 陆 续介绍 有关
的内容
3. 1 SQL Server 与 Access 的集 成
近几年 来 Microsoft Access 一种 桌面 数据 库产 品 已经作 为一 种前 端工 具应 用 于 SQL
Server 由于 它与 Microsoft Office 系统 产品 的集 成性 Access 简 化了依赖 于其 他 Office 应
用工具 中的 菜单 和工 具条的 用 户界 面 对那 些需 要在前 端 机如 Windows 9x 桌 面 或膝上电脑
工作的 用户 和编 程人 员来说 Microsoft Access 是个 不 错的选 择 Access 则为 热销 的 Office
系列产 品的 一部 分 数 以 百万计 的用 户已 经很 熟悉 它 了
Microsoft 最初的 Access 文 件格式 .mdb 使用 Jet 数 据库引 擎 它是 一种 决定 数据
访问系 统如 何在 磁盘 文件上 存 储数 据的 操作 系统 构件 与 MS Access 1.0 和 Visual Basic 3.0
一同推 出 的 Jet 引 擎是用来 处 理 小型数据 库 的 虽然这 几 年 它有所发 展 并 在 Access 2000
中得到 支持 但近 来 Microsoft 公司 正在 努力 促进 用 户使 用 OLE 数 据库和基于 SQL Server
的数据 库在 Office 2000 中 用户 可以 像访 问本 地的 MDB 格 式文件 一样 访问 SQL Server 中
文件格 式 .mdf 使用 Access 早 期 版本的 用户 仍然 可以 利用 ODBC 连 接远程访 问 SQL
Server 数据库 这点 不利 于产 品的 集成 因为 它只 是简 单地 将 MS Access 链接 到 真 实数据
库上 通过 本节 读者 将了解 到 MS Access 的一些 特性 这些 特性 允许 用户 SQL Server 使用
些桌面 数据 库产 品
随着 Access 2000 的到 来 SQL Server 集成也发 展到了 一 个新 的阶 段 Access 2000
可以将 SQL Server 看作一个 后端 存储 back-end store 使其 作用 于一 个新 的 Access 项目
以便 产 生 一个真正 的 客 户 端/ 服务 器模 型 实 现远程访 问 Access 项目 带有.adp 扩展 名的
文件 通常 包括 表单 报表 宏 构件 Data Access Page 见下一 节 创建 Access 项目
这些项 目一 般 不 直接 含有表或 查询 而是直 接与 包含 数 据 库对 象 例 如表 存储 过 程 视图
数据 库 图 等的 SQL Server 后端存储 相连 这里 值 得注意 的是 存在 相互 独立 的 客户端
Access 用户 和服务 器 SQL Server 构件 用户 接口 和 数据构件 业务 规则 的分 离
性是 client/server 或 N 层 开 发的一 个重 要特 性
在 Microsoft Access 2000 中 微软 公司附 带了 SQL Server 7 的一 种 可以用 作后 端存 储
的个人 版 然而 对 于拥有 SQL Server 7 的完 全版 或 SQL Server 2000 的用 户 还是应 该安
装完全 版 用户 可以 将 SQL Server 和 Access 集 成用于 Windows 95 Windows 98 或 Windows
NT 4.0+的平 台上 值得 注意 的是 Access 中的 SQL Server 版本根本 不提 供用 户界面 例如
企业 管 理 器 当然 这 也 不算 什么 大问 题 因为 Access 也 可 以为 用 户使用 提 供 不 错 的可
视化界 面 一般 地 如 果 同时使 用 Access 2OOO 和 SQL Server 很可 能将 Access 作为前
端 Access 可以为 各 种 不 同类 型 的 用 户 提供很友好的 用 户 接 口 UI 这一 点不 同于 企业
管理器
3.1.1 创建 Access 项目
为了创 建一 个 Access 项目使 用 SQL Server 作为 后端存 储 过程 只要 启动 Access 并
从对话 框中 选定 Access Database Wizard Pages 和 Projects 就 可以了 每次 选定 都开 启一 个
新的对 话框 以便 从常 用 选项中 选取 以下 内容
数据库 创建 一新 的 Access 数据库 MDB 文件
Data Access Page 创建 一新的 Data Access Page 一种 Microsoft 的 Internet Explorer
5.0 专用 的网 页 以便 将客 户端 数据 绑定 到活 跃的 远 程数据
项目 存在 的数 据库 打 开一个 已创 建的 Access 项目
项目 新数 据库 创建 一 个新的 Access 项目 ADP 文件
为便于 了解 可以 先创 建 一个连 接到 Pubs 数据 库的 Access 项目 从上 面介 绍过 的选
项中选 取 Project Existing Database 找到一 合适 的 文件夹 以便 保留 项 目 .adp 文件
选定了 项目 名之 后就 会出现 一 个 OLE DB 数据链接 属 性的对 话枢 通过 该对 话框的 指导 就
可以连 接到 SQL server 数据库上了
Microsoft 公司随 着 Windows 2000 和 Access 2000 的推 出提出 了数 据链 接 data link
的概念 数据 链接 对于 OLE DB 来说 相当 于 ODBC 文件 Data Source Name DSN 它允
许诸如 ASP Active Server Pages We b 页面 Visual Basic 程序 这样 的客 户端 连接 到与 之相
容的数 据源 OLE DB 上 一般地 这些 数据 源都 是数据 库 而且 OLE DB 可以扩 展 ODBC
以便于 支持 其他 类型 的文档 如 email 等 不论 何时 使用 数据 链接 与使 用 ODBC DSN 很
相似 它们 都是 连接 到 SQL Server 并提供 必要 的登 录信 息
Access 2O00 的 Data Link Properties 对话 框可 以提 示用 户要 连接 到哪 个数 据库 举例 来
说 用户 要连 接到 与 Access 处于 同一 机器 上的 SQL Server 的 Pubs 数 据库上时 可以 在对
话框中 提供 该机 器名 或 local 作为 SQL Server 名 填 写完对 话框 后 Access 显示出 Access
项目 再选 定 Pubs 数据 库 必须提 供的 表格 存储 过程 和数 据库 图表
使用 Access 项目 的一 个好处 就 是操 作 SQL Server 数据库 就像操 作本 地 MDB 一样方
便 SQL Server 数据库 提供 的标志 其 MDB 文档 的方 法很 方便 用户 使用 并且 Access 接口要比企 业管 理器 或其 他 SQL Server 工具提 供的 接口 高级 得多 在 3.l.2 节中 会介 绍一 些
Access 为 SQL Server 提供的一 些特 性
3.1.2 使用 Access 项目
将 Access 作为 SQL Server 前端的 一个 最明 显的 好处就 是 Access 为 用户数据 库提 供了
一个清晰 而有条理的视 图 在企业管 理器 用户常常 面对一 些令人困惑的 视图树 的结点 数
组 而这 些结 点包 含了 许 多对开 发者 毫无 意义 的选 项 并不 是每 个人 都需 要或 想 要管理 SQL
Server 对于 只要 任务 得以完 成 的开 发者 来说 他 们 只要对 单个 SQL Server 数据库作 些 处
理 而不 必对 企业 管理 器 提供的 管理 选项 加以 操作
Access 为 用 户 提 供 了 关 于 所 需信息的数据库级 视 图 以及 使 工 作 得以完成的工具 实
际上 用户 要做 的每 件事在 ACCCSS 中都可以 很容 易 地实现 从创 建视 图和 存 储过程 到
创建可 视化 展示 表及 表间相 关 性的 数据 库图 由于 在 Access 中 可以获 得这 些特 性 大多 数
开发者 有理 由选 择 SQL Server 的完全 版 用户 通过 Access 操作 SQL Server 数据库 并且
在 Access 中的 操作 引起 的 数据库 变化 直接 体现 在企 业 管理器 中 所以 可知 用户 是直 接对
主数据 库本 身进 行操 作 而不是 操作 该数 据库 的一 个 本地副 本
下面讨 论的 工作 在 Access 2000 中都可 完成
使用表
用户可 以在 Access 中创 建 打开 设计 删除 修改 SQL Server 表 在 Access 中使
用 SQL Server 表与使用 基于 Jet 的表很 相似 SQL Server 表的窗口看 上去 感觉和 Access
的窗口 差不 多 都有 Design 视图 和 Datasheet 视图 Design 视 图允许用 户设 计表 的字 段结
构 不过 要设 计字 段的 属 性不太 容易 对基 于 Jet 的 MDB 数据 库 Design 现 图 窗口的底部
增加了 Field Properties 区域 以便 于用 户改 变当 前字 段的 数据类 型 格式 和其 他属性 但对
SQL Server 数据库 用 户必 须从提供了 所有 字段的独立 的 属性窗口 或下 拉列表框中 访 问 各
个字段 不过 这也 没什 么 困难 只是 方法 不同 罢了 对于 SQL Server 数据库 它与 企业 管
理器中 的 Design Table 视图 更一 致一 些
使用触 发器
用户可 以在 Access 的表 视 图中创 建 修改 删除 触发器 就像 在企 业管 理器 中对 触发
器的操 作一 样 Access 中 的触发 器编 辑器 与企 业管 理 器中的 差不 多 只是 Access 缺少 Check
Syntax 选项
使用视 图
在 Access 中访 问视 图实 际 上与企 业管 理器 中进 行的 动 作很相 似 在 Visual Studio 97 中
首次引 进的 View Designer 窗口 已经 移植 到了 Access 2000 SQL Server 7 SQL Server 2000
以及 Visual Studio 6.0 并 得 到更新 因此 任何 熟悉 这些 工具 的人 都能 方便 地协 同工 作 只有
一个不同 点 在所有其 他工具中 视 图设计器包括四 个区域 分别显示表 图 字 段栅栏
原始 SQL 以及组 成这 张视图 的 查询 结果 而在 Access 中 由于 Design/Datasheet 模式的使
用 只有 前三 个区 域是 可 见的 第四 个区 域可 以通 过 从 Design 视 图切换到 Datasheet 视图
看到 使用数 据库 图
这 一 功能 也是 Access 2000 从企 业 管 理 器 的 类似功能中借鉴过来的 数据 库图 从外观
上来说 和产 生自Visual InterDev 和 FrontPage 之类工 具生成 的 Web 站 点形成的 视 图很相 似
可以用一种最简单的方法显示出表连接很复杂的数据库结构 依赖关系清楚地表示出
Access 能将 用户 在可 视化环 境 中所 要执 行的 功能 转化成 SQL 语 句序列
使用存 储过 程
存储过 程毫 无疑 问是 SQL 开发者必 备的 工具 Access 2O00 中 的存储过 程 设计 器
看起来 和企 业管 理器 或 Visual Studio 6.0 中的使 用不 同色 彩作 标记 的编 辑器 很相 似 存储 过
程的 执 行 也 和 企业管理器 中类 似 如 果 存 储过程需要 参数 Access 将 在 运行该过 程 以前 提
示用户 输入 测试 值 这 种 方法非 常方 便
使用表 单
Access 中 的表 单和 企业 管 理器中 的任 何对 象都 不真 正 具有一 对一 的关 系 熟悉 Access
的用户知 道 Access 应 用 程序是 一种 表单 的典 型应 用 用户 可以 借助 于类 似 Visual Basic
表单设计 器的工具来创 建数据库的访 问前端 如果 使 用向导 它 将 帮助用 户生 成 可以投 入
使用 的 表 单 的 基本框架 Access 有一 个表 单类 型 库 其 中包括 各种 可以 添加入表单 中的字
体 颜色 和背 景图 案的 组 合 Access 的表 单在 SQL Server 中并不存 在相 应的 对象 因此 对
企业管 理器 而言 Access 的任何 表单 都是 不可 用的
使用过 Visual Basic 或 Microsoft Office 所带的 VBA Visual Basic for Application 编辑
器的用 户应 该很 熟悉 Access 表单 其 环境 从本 质上 说 也不过 是控 件的 拖放 和修 改 包括 命
令按钮 文本 框 检查 框以及与底层 数据库结构相关 的控件 表单完成后 将作为 用户访 问
数据库的 前端 使用户 拥有一个可修 改的 总的来说 简单的 办法来查看和 修改数 据 对程
序员来 说 这是 使用 Access 采访 问 SQL Server 的 最 好理由 过去 Access 表 单 只能访问
Access 数据 库 目 前的 集 成度已 使得 它有 能力 访问 SQL Server 数据 库了
使用报 表
像 Access 表单 一样 报表也 是 Access 的特 性之 一 报表 的设 计也 和表 单相 同 可以
通过按 步骤 进行 的向 导 也可以 通过 Visual Basic 式 的设计 环境 手工 设计 报表可 用于 生成
数据库 信息 的统 计功 能 这 对用户和 管理 来说 都是 很 适用的 使用 SQL Server 提供的 工具
来创建 报表 几乎 是不 可能的 尽管 某些 极为 敬业 的 SQL 程 序员这 些年 来一 直用 Visual Basic
或其他 工具 来创 建他 们自己 的 报表 用过 Access 的用 户一定 会觉 得使 用 Access 来建报 表
是多么 明智
使用 Data Access Page
Data Access Page 也许 是 Access 2000 带 来 的最 激动人心 的新 特性 它要 求用 户具 有
Internet Explorer 5.0 Access 2000/0ffice 2000 包含 有 因为 它使 用的 客 户 方数据 绑定仅 对
该测览 器有 效 实际 上 Data Access Page 是一 种客 户方 的 不像 基于 ASP 是 服 务器方的
HTML 文档 它包 含动 态 HTML 脚本和 Microsoft Design Time ActiveX 控件 DTC 可
为 Access 提供 基于 表单 和 We b 的应用 程序 Data Access Page 可 做得和 表单 一 模一样 只不过可 以在 Web 浏览 器中运 行 通过 这种 方式 用 户 可以经 由 Internet 或本 地 Intranet 来访
问 而不 需要 在他 们的 机 器上运 行 Access Data Access Page 也 提供安 全保证 用户的 数据
库登录 及口 令并 不是 组成页 的 HTML 代码的 一部 分
Data Access Page 和 SQL Server 中的任何 Web 特性都 无必然 联 系 而是 对它 们 的扩充
不过 Data Access Page 需要 Internet Explorer 5.0 支持 而 SQL Server Web Assistant 产生的
任何 Web 页面 都能在 几乎所 有 We b 测览器 中使 用 3.l.3 一 节中将重 点介 绍 Data Access
Page
3.1.3 创建 Data Access Page
要创建 Data Access Page 必须首 先打 开一 个 Access 项目 并链 接到 诸如 Pubs 的 SQL
Server 数据库 在 数 据 库窗口的 工 作 对象列表 中 将 出现一个 名 叫 Pages 的选 项 这一选 项
即可使 用户 通过 向导 或者通 过 Design View 窗口 创建 新的 Data Access Page 也 可以使用
户编辑 一个 已存 在的 Data Access Page
使用 Data Access Page Wizard
Data Access Page Wizard 是创 建数 据相 关的 网页 的最容 易 方法 仅仅 选择 Create Data
Access Page by Using Wizard 选项 然后 按照 向导 中的 各步 选择 就 行了 这一 切和 先前 提到
过的 View Wizard 类似 尽 管用户 可以 仅在 Access 环 境中使 用产 生的 Data Access Page
Access 同时 也将 HTML 文件及 其相 关文 件 包括 XML 数据 图像 文件 及其 他 保存 在 My
Documents 文件 夹中 用 户还可 以将 结果 文件 发布 到 Web 服 务器上 允许 其他 人使 用 Office
2000 新的 Web 文件 夹特 性通 过 Internet 访问
使用 Design 视图
Design 现图 的使 用需 要在一 定 程度 上熟 悉 Design 视 图编辑 器 这一 编辑 器正 如 表单编
辑器一 样与 Visual Basic 风格 类似 实际 上 用户 必 须在栅 栏化 的表 单中 添加 控 件 并将 其
与数据库 中的对象相关 联 和使用向 导相比 这一 过 程相当 乏味 但用户 却能由 此获得 一
些在向导 中无法得到的 可修改性 一 种推荐的做法是 用向 导生 成 页 然 后在 有 必要修 改
时在 Design 视图 中进 行 除非只 有一 个很 简单 的数 据 库 否则 使用 Design View 来设计 Data
Access Page 通常 是一 项很大 的 工程
3.1.4 Microsoft SQL Server 的双 向数 据复 制
在 Access 97 中 数据 可以 从 Access 数据库 复制 到 SQL Server 中 而从 SQL Server 向
Access 数据 库复 制数 据则是 不 可能 的 这种 由 SQL Server 发布者 Publisher 向基于 Jet
的 Access 订阅 者 subscriber 的单 向数 据复 制功能 在 Access 2000 得 到了改进 现在
数据可 在 SQL Server 和 Access 之间 双向 复制
注意 注意 注意 注意 这一 特性 当然 是有 限制 的 只有 类似表的精 确数据才 能被 复制 表单 报表和
其他非 数据 对象 是不 允许复 制 的
有趣的 是 Access 2O00 和 SQL Server 之间的 复制 冲突 现在 可以 由 SQL Server 的冲突
解决向 导 Conflict Resolution Wizard 来处 理 它视 SQL Server 的 一部分 而不 属于 Access
冲突解决 向导用一张冲 突表来存储那 些不能传达到服 务器的 改变 它根据 内部基 于优先 级
的冲突 解决 算法 来决 定哪些 改 变允 许执 行 哪些 不允许
3.2 SQL Server 与 Excel 的集 成
从 Excel 20O0 开始 使用 SQL Server 也包括其他各种数据库 作为数据透视表
PivotTable 或 Pivot Chart 的数 据源 成为 了可 能 数 据透视 表允 许用 户动 态过 滤大 量 数据
以使显示 能随操作实时 改变 当用户 进行一大组相关 数据集 的比较时 数 据透 视 表就显 得
更为 有用 了 数据 透视 图 Pivot Chart 用 图形方式 提供 了相同的 功能 使大量 数据 的操
作能通过 图表进行 虽 然数据透视表 的创建并不需要 相关的 数据透视图 后者 通 常总是 与
一个数 据透 视表 相关 连 并且必 须位 于同 一个 Excel 工 作本中
创建 Excel 数据 透视 表
要创建数据透视表 可以在 Excel 中找 到合 适的 工作 本 然后 在菜 单中 选择 Data 的
PivotTable and PivotChart Report 激活 PivotTable 和 PivotChart 向导 它 将向 用 户询问 要使
用的数 据类 型 这有 四种可 能
Microsoft Excel 列表 或数 据库
外部数 据源
多联合 域 Multiple consolidation ranges
另一个 PivotTable 或 PivotChart 仅自 己创 建时 才可 用
为使 用 SQL Server 必 须 选择外 部数 据源 向 导的 第二步将 要求用户 使用 Microsoft
Query 来查找数 据源 该 部件必 须已 安装 Choose Data Source 对 话框允许 用户 选择 一个
已创建 的 ODBC 数据 源名 DSN 创建 一个 新 DSN 或 创建一 个特 殊的 查询 如果要 创建
新 DSN 就需 要按 向导 的指 引一 步一 步来 在 本例 中 将创建 一个 名叫 northwind 的 DSN 指
向 Northwind SQL Server 数据库 从这 开始 向 导将提 示 选择 查询 中所需 的字 段 所列 举
出的 Northwind 数据 库中有 若 干表 包括 Categories Category Sales for 1997 等等 当选
好要 显 示 的字段时 向导将 提示用 户为 PivotTable 选 定 工作 表 位 置 或者 如果用 户从一
批没被连接 上 的 表中选择了字 段 Microsoft Query 则 字被启 动 来完成手 工 连 接 与其使 用
Microsoft Query 不如 退回去重 新考 虑一下 因为 在 不相关 的数 据基础上 生成 逻 辑表是 很
困难的 事情 在此 用 户 仅需将 页域 列域 行域 以 及数据 项拖 入 PivotTable Excel 将像
平时与 原始 数据 协同 工作一 样 与 PivotTable 协作
3.3 在 IIS 和 IE 中使 用SQL Server
作为 Microsoft .NET Enterprise Servers 套件 中基 于 Windows 服 务器的关 键成 员 SQL
Server 与其他 Microsoft 服务 器产 品都 有良 好的 集成 其中 包括 Internet Information Services
Microsoft 的 Windows DNA 战略的 关键 概念 之一 就是 分布 式应 用 IIS 是目前为开 发分 布式
应用的 最合 理的 手段 通过 World Wide Web
Web 的发 展将 SQL Server 的开发 者带 入一 种有 趣的两难 境 地 现在 Microsoft 技术提
供 了 两种 可能 的途 径向 客 户 方传 送应 用 一种是基于服务器的通常的方法 例如 Active
Server Pages 技术 可以 在任 何操 作系 统平 台的 浏览 器 上使用 处理 主要 由服 务方完 成 另一种方 案 来 自 Internet Explorer 5.0 它 使 用 户 能 将 问题交由客户方的数据 库 逻 辑完 成 从而
减轻了 服务 器的 压力 两 种方法 各有 千秋
3.3.1 在服 务方 访问 数据 库
在今天 的 We b 上 客户 之 间仅有 一小 部分 公用 的功 能 可以依 赖 不同 的 Web 刘览器
运行在 不同 的操作系 统之上 甚至 底层 的硬 件 平 台也不 同 从而 提供 的功能也千 差万 别
因此 Microsoft 开发 广 ASP 允许 We b 开发者 通过 服务 器 IIS 插件执行 数据 库访 问及 其他
功能 然后 将纯 义本 传回给 例 览器 同为 传给 浏览 器 的 信息是以 文本 形式 存在 的 任何 Web
浏览器 都能 接受 因此 We b 开发 者就 无需 考虑 兼容 性 问题 例如 大多 数 ASP 代码都 是
用 VBScript 编写 的 而它与 Netscape Navigator 不兼容 当 Web 页 面到达 浏览 器 时 已看
不到任 何 VBScript 的 影子了
3.3.2 使用 Internet Explorer 进行客 户端 数据 库 访问
另一 方 面 如果所 有 客 户 端 都使 用 Internet Explorer 5.0 或 稍早一点 IE 4.o1 with
Service Pack1 由 于这一浏 览 器 具有客户方数据 相关 的 特性 用 户就可 以 使 用一套全 新 的
功能 让 客户方有能力 处理实时数据 将使服务器从繁 重的工 作中解放出来 并 使 用户处 理
变得更 加有 力 更加 具有动 态 性
当然 很少能 保证所有用户都使用同一种 浏览器 当前 客户方的数 据绑定服务仅在
Internet 上有意义 因为 此时 Internet Explorer 的选 择可 以作 为一 种标 准
注意 注意 注意 注意 客户方数 据绑 定技 术概 念最 早出 现在 IE 4.0 中 第一个 使用 这一 概念 的 技术是
远程数 据服 务 Remote Data Service RDS 随着 Visual Studio 98 的发 行
另一种 此概 念的 应用 成为现 实 那就 是设 计时 控件 Design time Control
3.4 SQL Server 与 Microsoft Transaction Server 集成
Microsoft Transaction Server MTS 是 Internet Information Server Microsoft Web 服务
器的组 成部 分 用户 可通过 访 问 MTS API 来 开发 自 己的软 件构 件 DLL 文件 将业 务逻
辑分解成 事务 Transaction 事务 概念 对 SQL Server 开发者来说应当尽快熟悉
Transaction Server 事务正如 SQL Server 事务 一样 要 求完整 地执 行 或者 整个 成 功 或者
整个失败 考虑下面这 个经典的银行 存取结余事务 它由两 个不同的动作 构成 取和存
然而 作 为一次成功的 传输任务 两 种操作都需要发 生 如 果其中任何一 个失败 则整 个
事务回 滚 如同 两个 操作都 没 发生
注意 Windows 2000 将 MTS 重命名 为 Component Services 组件服 务 从 而更为 贴
切地反 映出 这一 技术 的用途 对 COM+/Windows DNA 体 系结构来 说 Component
Services 是一 种关 键的 管理 工 具
在 Transaction Server 出现以 前 程序 员不 得不 手工 实 现这一 行为 数据 库开 发者在 存
储过程 中使 用 BEGIN TRANSACTION 和 COMMIT TRANSACTION 等 T-SQL 命 令 来实现
SQL Server 的事务 处理 如 果这种 基于 SQL Server 的事 务中 有一 项操 作失 败 则整个 事务
将要回 滚 如果 有必 要的话 SQL Server 事务还 可以前 滚 Microsoft Transaction Server 继
承了 SQL Server 的事务 思想 并运 用于 其他 技术 实现 上 和 SQL Server 一样 Transaction
Server 与 Microsoft Distributed Transaction Coordinator MSDTC 交互以 确认 事 务满足 ACID
完整 性 Atomicity 一致性 Consistency 分离性 Isolation 和耐 久性 Durability 测试
MTS 与 IIS 集成发行 而且 通常 与其 相关 因为 We b 向 开发者 提供 的是 一种 无 状态的
环境 典型 情况 下 Web 客户端 连接 一个 远程 资源 如 Web 页面 或是 通过 Web 页面访
问的远 程数 据库 一旦 所有 数据 传输 完就 立即 断连 Web 正 是依靠这 种不 连续 的连 接才 能
保持速 度可 接受 如果 We b 客户 保持 与服 务器 的持 续 连接 服务 器的 需求 将更 大 处理 能
力也要 更强 同时 整个 Internet 本身 的结 构也需 要升级 在 Transaction Server 的帮助 下
这种无 状态 的 We b 环境 就 是可避 免的 一组 操作 事务 的无 错性 就能 得到 保证 如同 SQL
Server 事务一样 MTS 事 务中的 一个 操作 失败 整个事务就失败 更重要的是 这一 功
能由 MTS 隐含 完成 用户 不必 开发 自己 的相 应操 作
3.4.1 MTS 事务 和 IIS
从 IIS 4.0 开始 Active Server Pages ASP 服务 方脚 本允 许访 问 MTS 特性 IIS 3.0
中脚本 还不 能访 问 MTS 特性 We b 开发 者由 此可 以 创建多 层 n 层 client server 方式
的 Web 应用 一个多 方 Web 应用 通常 包括 下列 三层
表达层 基于 HTML 或 DHTML 的用户 界面 基干 Web 浏 览器的 用户 界面 使用 户
能够通 过丰 富的 Web 表 单 和控件 与 We b 应用程 序进 行交 互
业务 逻辑 层 决定 应用 程序如何 进行 的规 则 业务 逻辑是用 来连 结数 据仓 库 data
store 如 SQL Server 和表达 层
数据服 务层 一般 是一 种结 构化 的数 据仓 库 例如 SQL Server 或 oracle 多 个数据
服务层 可供 一个 Web 应用使 用 这一 服务 由中 间层 业务逻 辑层 的 MTS 构件
管理
使用这种 三层策略 通常将数据与表达 层分离 而 MTS 构 件或脚本则工作在这两层之
间 提 供 逻 辑上的空 间 满足 这种分 离 并 决定 另 两 层如 何 交 互 Microsoft 的 这 种将客户 服
务器模 式开 发与 Web 开 发 合成到 一起 的战 略被 称为 Windows Distributed InterNet Application
Architecture Windows DNA 它使用 HTML 动态 HTML DHTML 和 Active Server
Pages 向用 户提 供第 一层 表达层 而 Microsoft Transaction Server 和使 用 MTS 的构件
如 IIS IIS/ASP 构件 以及 ASP 脚本等 占据 了中 间层 业务逻 辑层 SQL Server 通常作 为
数据服 务层 尽管 其他 诸 如 Oracle Microsoft Access 或 Exchange Server 邮件 包等 工具 也能
有限地 使用 要与 MTS 集成 数据 仓库 必须 支持 X Open 组 织提出 的 XA 协议 SQL Server
当然是 与 MTS 合作 的首 选数 据库
注意 注意 XA 是一 种由 X/0pen DTP 组 织定义 的两相提 交协议 允许多个 数据库服 务器间
注意 注意
的 事 务协 同 并将 其看 作 一个 事务 这一实现保证 了本与事 务的 所有 服务 器上
的数据 库全 部更 新 或 者 没有一个更 新 许多Unix 数据 库 包括Oracle Informix
和DB2 都完 全支 持 XA 协议
用 MTS 编写 ASP
通过使 用 TRANSACTION REQUIRED 指令和 ASP 的 Server 对象 Transfer Execute方法 用户 可以 充分 利用 MTS 的优 势来 编写 ASP 脚本 下面 的代 码段 说明 了在 ASP 中如
何初始 化一 个事 务
<% @TRANSACTION=REQUIRED %>
<%
‘Begin transaction
‘Code that must occur in a transaction
‘End transaction
%>
这段代 码中 有几 点需 要注意 代码 代表 的是 一整 页 ASP TRANSACTION 指 令 必须置
于页首 第一 行 它提 醒 ASP 编译器 代码 所访 问的 所 有 ASP 和 构件都是 同一 事 务的不 同
部分 TRANSACTION 指令 的使 用格 式为<% TRANSACTION=Va1ue%> 其中 Va l u e 是
表 3-1 所示 各值 之一
表3- 1 TRANSACTION 指令的可选 VALUE 值
值效果
Required 指令后的脚本将初始化一个 MTS 事务
Required_New 指令后的脚本将初始化一个 MTS 事务
Supported 指令后的脚本不初始化 MTS 事务
Not_Supported 指令后的脚本不初始化 MTS 事务
一个包 含 MTS 事务 的 ASP 页面通过 Server.Transfer 或 Server.Execute 方 法从另一 ASP
页面中 调用 Transfer 办法 简单 地将 当前 会晤 的控 制 传递到 另一 ASP 页面 第一个 ASP 页
面中创 建的 任何 对象 和变量 都 传送 到第 二个 ASP 页面 中 Transfer 方 法格式 如下
Server.Transfer ”path”
代码中 的”path”是指 第二 个 ASP 页面 的绝 对或 相对 路 径 当使 用事 务型 ASP 页面 以
TRANSACTION 指令 开头 时 用户 通常 使用 Transfer 方 法来引用 事务 页面 当事务
页面运 行结 束后 控制 传 回调用 的页 面
Server 对象的 Execute 方法 也能 使用 它调 用另 一 ASP 页 面时不传 送任 何参 数或 对象
Execute 方法 的调 用格 式与 Transfer 办法相 同
Server.Execute ”path”
其中 path 的意 义也 相同 不管 使用 server.Transfer 还是 server.Execute 第二个 被
调用 的 ASP 页面都应在 第 一行包含 TRANSACTION 指令 然后 当文 件中 的代 码执行
完后 它 按照 调用 时相 同 的规则 将控 制返 回给 调用 页
事务型 ASP 最后 一个 要注意 的 地方 是 事务 型 ASP 页 面能处理 两种 Transaction Server
事件 OnTransactionAbort 和 OnTransactionCommit 具体 处 理 哪个事务当然 取 决 于 事 务是
否成功 完成
在此介 绍一 个简 单的 ASP 页代码 样例 transactl.asp 它引 用名 为 tansact2.asp 的文件
<% @ Language VBScript %>


Test transaction< TITLE><br> < HEAD><br> <BODY><HI>Test transaction</HI><br> <%<br> Server.Execute ’tansact2.asp’<br> %><br><P>This appears after the transaction is complete<br>/BODY<br> </HTML><br>被调用 的页 见下<br><% @ TRANSACION=REQUIRED%><br><%<br>Response.Write”<H2>This comes to you from within a transaction </H2>”<br> Sub OnTransactionAbort<br> Response.Write ”Transaction unsuccessful ”<br> End Sub<br> Sub OnTransactionCommit<br> Response.Write ”Transaction successful ”<br> End Sub<br> %><br>输出结 果首 先是 Heading one 字体 的 Test transaction 段 如 transactl.asp 所示 然后 控<br>制转入 事务 页面 transact2 它首 先打 印一 行 Heading Two 字体 然后 在事 务成 功 完成时 打<br>印一条成功信 息 控制再返回调用文件 transactl.asp This appears after the transaction is<br>complete. 信息显示出来 显然 大多数 事务 代码都比 这复 杂 但总 体结 构大致相 同 下一<br>节 我们 将讨 论 ASP SQL Server 和 MTS 的用 法<br>3.4.2 MTS 事务 和 SQL Server<br>既然 SQL Server 本身提 供事 务处 理功 能 为什 么还 要在 访问 SQL Server 的主 页中 使 用<br>MTS 呢 要记 住 只有 在 SQL Server 中一行行 地遵循 Transact SQL 规 范才能 自动 提交 事<br>务 如果 希望 显式 的强 制 使用事 务处 理 用户 必须 使 用 Transact SQL 代码 包括 BEGIN<br>TRANSACTION COMMIT TRANSACTION ROLLBACK TRANSACTION 等 以保证<br>代码正 常运 行 同 时如 果 失败还 可以 回滚 然而 如 果使用 Active Server Pages 和 ActiveX Data<br>Objects ADO 来编 写 SQL Server 应用 一般应 使用 ADO 的 对象模型 来操 作 数据 而<br>不是 Transact-SQL 代码 例如 在 ADO 中 用户 可 以使用 INSERT 的 Transact-SQL 语句<br>向表或 视图 中添 加新 行 但使用 Recordset 对象的 AddNew 方 法将更有 效一 些 类似的 操<br>作 包括 行删 除 修改 添加等 显然 都适 用于 事务型 ASP 页面 而另一 方面 如果 有条<br>件使用 Transaction Server 和 ADO 来幕后完 成事 务处 理的 话 编写 复杂 的 Transact-SQL 代<br>码就成 为不 必要 的事 情了 没有 理由 无视 Microsoft 提 供的易 用工 具而 坚持 把时 间浪 费在 显<br>式地编 写事 务处 理代 码上<br>3.4.3 强制 分布 式事 务处 理<br>SQL Server 也能提 供内 建的 Transaction Server 服务 要认识 这一 点 可 以打 开企业 管<br>理器 右击 想修 改的 服务器 选择 Properties 在弹 出的 对话 框中 选择 Connection 页 单击<br>Enforce Distributed Transactions MTS 单选 按钮 如果 Microsoft Transaction Server 对 SQLServer 不可用 这一 选项 将是 无效 的<br>3.4.4 MTS 事务 处理 和可 视化 编程 工具<br>Microsoft Transaction Server 也能经 由 Microsoft 可视 化编 程工 具访 问 其中 包括 Visual<br>Basic Visual C++ 和 Visual J++ 实际 上 开发 者将 业务 逻辑 层封 装在 Visual Basic Visual C++<br>或 Visual J++ 写的 MTS 构件 中 其效 率和 通用 性都比在 事 务型 ASP 页 面中使用 要好 当然<br>这一内 容不 在本 章的 范围内 但作 为真 正的 Windows DNA Web 应 用程序 必须 使用 MTS<br>构件来 充当 客户 端 可以是 We b 训览 器 也 可以 是真实 的 Win32 程序 和 SQL Server 数<br>据源 之间的中 间层 即 业务逻辑 层 通过将数 据访问 构件 封 装入 Transaction Server 它们<br>就会随 着网 络的 变化 自动伸 缩<br>3.5 小 结<br>本章介 绍了 如何 使 SQL Server 与 Microsoft 其它 产品集 成 重点 介绍 了 Access 2000<br>Excel IIS IE 和 MTS 如何 与 SQL Server 协调工作 并且 比较 了 Access 97 和 Access 2000<br>与 SQL Server 集成时的 差别 特别 是连 接和 双向 复制 方面 我们 还学 习了如 何在 SQL Server<br>数据上 创建 Excel PivotTable 如何 在 IIS 作为 Web 服 务器时 将 SQL Server 作为数 据层 最<br>后 我们 了解 了 SQL Server 能使用 MTS 来 控制 分布事 务 并保 证数 据的 跨多 服 务器一 致第4 章 使用Access 访问SQL Server<br>在本章 中 将学 习如 何使用 Microsoft Access 的强大 的数 据库 设计 和开 发功 能创 建利 用<br>SQL Server 的高性 能 多用 户数据 库的 客户 机 服 务 器应用 程序<br>4. 1 概 述<br>Microsoft Access 既 是一 个 独立的 数据 库平 台 也 是一 个强大 的数 据库 开发 工具 可以<br>用来 开 发 定 制 的客户机 服务 器数 据库 应用程序 Access 包含 了一 套丰 富的数据库 开发工<br>具 允 许 创 建 定制的数据 库应 用 程 序 Access 主要是作 为一个 独立 的数 据库 然 而 在本 章<br>也可以 看到 如何 使用 Access 开发 SQL Server 的数据库 应 用程序 随着 Access 2000 的发布<br>SQL Server 与 Access 之 间 的集成 性能 有了 明显 的改 善 本章 将以 Access 2000 为 背 景介绍<br>使用Access 开发 SQL Server 数据库 应用 程序 的方 法<br>作为一 种数 据库 平台 Access 使用 Microsoft Jet Database Engine 处理 其 存储 和查询 功<br>能 Access 数据 库存 储在.mdb 文件 中(Microsoft Database) 这些 文件 一般 存放 在 PC 的硬<br>盘驱动 器上 或者 网络 共享上 Jet 引擎主 要用 于处 理单 用户 数据 库 它也 可以 用 于比较 小的<br>多用户 应用 程序 由 Access 使用 的 Jet 引 擎 包含 一个功 能 完整 的查询处理 器 它回 应 Jet SQL<br>语句 Jet SQL 语句的语 法虽 然类 似于 SQL Server 的 Transact-SQL 语句 但是 两者 并不 相<br>等 就 像 希 望 数据库系统 所做 的 事 情 一 样 Access 的 查询 处 理器 可以 执行 查 询 检索 结 果<br>集 连接 文件 和执 行那 些 类似于 SQL Server 更加强 壮的 多用 户数 据库 执行的查询动 作 除<br>了 其 数据 库功 能外 Access 提 供 了 一 个 可 视化 数据 库 开 发环境 它提供了一个可 视化 数 据<br>库设计工 具 允许快速 创建数据库 并且包含了一个 可视的 窗体创建器 可以 用 于创建 数<br>据输入 窗口 以及 图形 化的报 表 设计 工具 Access 的主 要可视 开发 工具 如表 4-1 所示<br>表4- 1 Access 的快速开发工具<br>组件 描述<br>Table Designer 用来创建或者链接表和视图<br>查询设计器 用来创建数据库查询<br>窗体 Designer 用来创建数据输入窗体<br>报表设计器 用来创建数据库报表<br>Macro Creator 用来创建数据库宏 可以用来自动化简单的动作<br>用来创建 Visual Basic for Applications 脚本 用于比较复杂的编程任务<br>VBA Editor<br>除了这 些主 要的 组件 Access 还 包括了 30 多个 向导 可以 用于 执行 许多 常见 的任 务<br>例如创 建窗 体 报表 和查询 图 4-1 提供 了 Access 环 境的高端 概览 如 图 中所示 Access<br>的核心是其本地的查询处理器 也称作 Microsoft Jet(Joint Engine Technology 简称<br>Jet)Database Engine 该 Jet 引擎自 己访 问存 储在 本地 的.mdb 文 件中的 数据 然而 使用 外<br>部的数据 库驱动程 序 Jet 引擎还可 以访问存 储在其他 数据库中的数据 这些 数 据库包 括<br>Paradox dBase 和 ODBC 数据 源 Access 的 前端开 发 工具通 过 Jet 引 擎与不同 的 数据库 联系起来<br>图4-1 Access 概览图<br>4.1.1 在多 用户 情况 访问 Access 和 SQL Server 的区 别<br>Access 能够 处理 多用 户 但在 Access 和 SQL Server 处 理 多用 户 的 方式上 有 很大的差<br>Access<br>别 使用 每一个系 统包含 其自 己的 查询处理器 在并 行的 数据 库访问上 没有 集<br>中式的 控制 或者 优化 图 4-2 示 意 了一个 多用 户的 Access 应 用程序<br>在一个 典型 的多 用户 Access 应用 程序 中 数据 库可 以 由运行 Access 的 多客户机 系统<br>共享和访问 在每一种情况下 查询处理 器位 于联网 的客户 机系统上—— 不 是数据 库本 身<br>联网 既 没有集中化的 机制来优化并 行的访问 又没 有任何 中央资源处理 其他资 源冲突 的<br>Access<br>锁定 问 题 数据库通 过系统 表协调多 个用户 这 些系统 表也 像数 据库表一样 面对<br>同种类型 的多用户冲突 问题 另外 为了满足查询和 其他数 据库的请求 客户 机 系统必 须<br>通过网络 执行网络文件 读 为本地查 询引擎提供数据 即使 在 一个快速网 络中 通过网 络<br>阅读数 据也 比使 用本 地的硬 盘 驱动 器访 问数 据要 快许多 倍<br>即使 如 此 对于 比较少的用 户 这些问题 也 是 可 管 理的 Access 可 以 有效地用来 创建<br>Access<br>部门 的 或 者 其 他 较 小规模 的 多用户应用程 序 然而 当数 据 访问请求增加时 不能<br>够伸缩来 满足这些要求 当涉及大量 的用户或者用户 数虽少 但是操作频繁 时 缺 乏优化<br>缺乏资 源管 理和 对网 络 I O 的要求 就使 多用 户的 Access 应 用程序的 性能 和可 靠 性大幅 度<br>地下降图4-2 多用户的 Access 应用程序<br>相对而 言 SQL Server 主要用于多 用户 数据 库访 问 使用 SQL Server 数据 库和查 询<br>处理器驻 存在中央数据 库服务器上 在客户机系统上 没有 本地的查询处 理器 相反 在<br>客户机 系统 上的 数据 库应用 程 序向 SQL Server 系统 发送 数据 库请 求 SQL Server 查询处 理<br>器接收所 有到达的请求 访问数据存 储和把每一个请 求的结 果返回给每一 个客户 机系统<br>4-3 SQL Server<br>图 提供 了使 用 访问多用 户数 据库 的概览<br>图4-3 多用户的 SQL Server 数据库访问<br>在一个 多用 户的 SQL Server 实现中 数据 和查 询引 擎位 于中 央 SQL Server 系统上 客<br>户应用 程序 使用网络 协议和 网 络 通 讯库 与该 服务器通讯 SQL Server 的 查 询处 理 器处理 所<br>有到达的 请求 分 析这些请求 并且 应用 处 理这些 请 求 的 优化 因为查询引擎和 数据库 一<br>般位于同 一个系统上 所以所有锁和 数据访问都非常 快并且 效率高 这种 客户 机 服务 器<br>SQL Server<br>体系结 构允 许 向上伸展 处理数 百个 或者 数千 个客 户<br>4.1.2 Access 作为 前端开 发 工具访 问 SQL Server<br>Access 主 要 用 于 创 建 独 立 的 单用户数据库应用 程 序 另一 方 面 它还可以用作客 户机<br>服务器 应用程序的开 发工具 相同 的图形开发功能 使它成 为一种高度的 个人数 据库开 发<br>工具 又使 它成 为开 发 SQL Server 数据库 应用 程序 的强 大工 具 链 接 它们 的 是 开放数据 库连接性(ODBC) 标准 ODBC 标 准 由微软 公司 开 发 通过 ODBC 驱 动程序 提 供 了访问 关<br>系型数 据库 的方 式 ODBC 已经被 广泛 采纳 并且 所有 主要 的数 据库 供应 商现 在都 支持 它<br>包括 Access 在 内的 许多 终 端用户 程序 也包 括内 置的 ODBC 支持 可以 在第 5 章得到 ODBC<br>更多的 信息 使用 SQL Server 的高性 能 多用 户数 据库 ODBC 可 以连接 Access 的高产<br>前端数 据库 工具 这样 就 允许使 用 Access 快速 简单 地 创建数 据 输 入窗体 以及访问 存储 在<br>SQL Server 中的数 据库 的查 询和报 表<br>4.1.3 Access 通过ODBC 访问 SQL Server 机制<br>在详细 论述 如何 连接 Access 和 SQL Server 之前 先看 看当 Access 使用 ODBC 连接到<br>SQL Server 数据库 时使 用的 网络体 系结 构 图 4-4 说 明了由 Access 和 ODBC 连接到 SQL<br>Server 使用的 网络 层次<br>图4-4 Access 使用 ODBC 的网络体系结构<br>在顶层 可以 看到 Microsoft Access 应用程 序 因为 Access 是 一种允许 ODBC 的应用<br>程序 所以 它可 以调 用由 ODBC Driver Manager 提供 的各种 ODBC API 函数 然后 ODBC<br>Driver Manager 使用 相应 的 ODBC 驱动程 序连 接到 目 标数据 源 在 SQL Server 中 就是 SQL<br>Server ODBC ODBC IPC( )<br>驱动 程序 驱动程 序使 用相 应的 网络 进程 间通 讯 方 法 与数据库 服<br>务器上 的相 应网 络库 通讯 网络 IPC 机制 允许 联网 的 系统与 另一 个系 统通 讯 两 个常用 的<br>IPC 机制 是 Named Pipes 和 TCP IP Sockets 客 户机的 IPC 机 制使用 实现 的网 络 协议与 服<br>务器的 IPC 通讯 网络协议负责发送和接收网络上的数据流 两种常用的网络协议是<br>NetBEUI 和 TCP IP 最后 在这 个 通 讯堆 栈的底端是 物 理网络 物理 网络 定义要 求 物理<br>连接客户 机和服务器系统 的硬件 物理网 络包括适配器卡 集线 器和 电缆 这些 用于连 接<br>分布式 的客 户机 和服 务器系 统 Ethernet 和 Token Ring 是 两个最常用 的网 络拓 扑 结构<br>4.2 Access 连接到SQL Server<br>为了从 Access 连接 到 SQL Server 必须满足 三个 主要的 要 求 第一 个非 常明 显 必须<br>从运行 Access 的 系统 中登录 到 SQL Server 中 这 就 要求在 Access 系统 和 SQL Server 系统<br>之间有 网络 连接 并且 增 加一个 到 SQL Server 系统 的登 录帐 户 接下 来 SQL Server ODBC<br>驱动程 序必 须安 装在 Access 系统 上 必须 创建 目标 SQL Server 系统 的数 据源 最后 必须<br>在 Access 中创 建一 组链 接 表 这些链 接表 使用 ODBC 连 接访问 SQL Server 的表4.2.1 安装 SQL Server ODBC 驱动 程序<br>Microsoft SQL Server ODBC 驱动程序 是由 Microsoft Office 或者 Microsoft Access Setup<br>程序安 装的 正如 所希 望 的那样 如果 要求 Access 作为 Microsoft Office 套的 一部 分 就需<br>要运行 Microsoft Office Setup 程序 如果 使用 独立 的 Microsoft Access 版本 就需 要运 行<br>Access 的 Setup 程序 然而 在默 认情 况下 SQL Server ODBC 驱动 程 序是不 安装 的 如<br>果使用“Typical” 安装选 项来安 装 Access 或者 Office 那么 SQL Server ODBC 驱 动 程序将 不<br>出现在 系统 中<br>注意 注意 注意 注意 也可 以通 过安 装 SQL Server Client Management 实用 程序 在系 统上 安装 SQL<br>Server ODBC 驱动 程序<br>为了对 已有 的 Access 安装 SQL Server ODBC 驱动程 序 需要重新 运行 Access 或者<br>Office 安装 程序 Office 97 中文 版安 装 程序显 示如 图 4-5 所 示的对 话框<br>4-5 Office 97 <br>图 中文版安装对话框<br>为了在 一个 已有 Access 的 系统上 安装 Microsoft SQL Server ODBC 驱 动程序 先单击<br>对话框 中的 添加 删除 显示 维护 对话框 如图 4-6 所示 选择 Data Acess 复选框<br>然后单 击 更改 选项 则显示 Data Access 选项对 话框 如图 4-7 所示<br>图4-6 安装程序的维护对话框图4-7 安装程序的 Data Access 对话框<br>在 Data Access 选项 对话 框 上 选择 数据 库驱 动程 序 复选 框 然后 单击 更 改选项<br>按钮 则显 示如 图 4-8 所示 的数 据 库驱动 程序对 话 框 选择 Microsoft SQL Server 驱动程<br>序 复选 框 然后 单击 确定 接着 Office 97 中文 版 安装程序 把 Microsoft SQL Server ODBC<br>驱动程 序安 装在 系统 上<br>图4-8 安装程序的 Database Drivers 对话框<br>提示 在 SQL Server ODBC 驱动 程序 安装 之后 它 可 以由各 种 Office 程 序用来 访问 SQL<br>Server 数据 库<br>4.2.2 配置 数据 源<br>安装 SQL Server ODBC 驱 动程序 本身 还不 足 以 开始从 Access 中使 用 ODBC 还需 要<br>创建一 个数 据源 为 了创建 SQL Server 的一 个 ODBC 数据 源 需要 从 Windows2000 的程<br>> > ODBC ODBC Windows 95/98<br>序 管理 工具 数据 源 运行 数 据源管 理器 假如 系统 是<br>或者 Windows NT 则要 从控 制面 板中 运行 ODBC 数据 源管 理器 打开 ODBC 数据源管理器图标 则显 示 ODBC 数据源 管理 器 对话 框 如图 4-9 所示<br>图 4-9 使用 ODBC 数据源管理器增加一个数据源<br>注意 有关ODBC 数据 源管 理器 提供 的选 项的 详细 信 息 参见 第 5 章<br>可以在 用户 DSN 标签 系统 DSN 标 签 或者文 件 DSN 标签 上 增加 一个 新数 据 源 如<br>果该数 据源 只是 用于 当前用 户 那么 选择 用户 DSN 标签 如果 该数 据源 可以 由 系统 中的 全<br>部用户 使用 那么 选择 系 统 DSN 标签 文件 DSN 标 签允许 创建 一个 可以 由其 他 用户共 享<br>DSN DSN<br>的 为了 配置 一个 新的 系统 数据 源 选择 用户 标签 然后 单击 添 加 则显 示 创<br>建新数 据源 对话 框 如图 4-10 所示<br>4-10 ODBC<br>图 选择一个 驱动程序<br>当前 安装 在 系 统上 的 所 有 ODBC 驱 动程序 都列在该 对 话 框 中 为了 安 装 SQL Server<br>ODBC 驱动 程序 从列 表中 选择 SQL Server 然后单 击 完成 这时 显示 SQL Server<br>DSN 配置 向导 对话 框 如图 4-11 所示图4-11 选择 SQL Server DSN 数据源名称<br>在该向导的第 一个文本框中 输入数据源 的名称 这个名称可以是任 意名称 其主要<br>作用是 为该 ODBC 数据 源 提供一 个有 意 义的名 称 说 明 字 段允许进 一 步地确 认 这个数 据源<br>接下来 从该 下拉 组合 框 中 选择 希望 该数 据源 连接的 SQL Server 系统 的名 称 所有 网络<br>上的 SQL Server 系统都 列在 组合 框中 名称“local” 用于 在 SQL Server 系统自身上的 ODBC<br>连接 一般 不使 用“local” 而是希望 从下 拉框 中选 择 联网的 SQL Server 系统 中的系 统 名称<br>单击 下一 步 按钮 则显 示 认证 对话框 如图 4-12 所示<br>图 4-12 指定 SQL Server DSN 认证<br>这种认 证允 许指 定将 要执行 的 SQL Server 的安 全性 认证 模式 如 果使 用集成 安全性<br>选择该 选项 可以 使用 Windows NT 认证 它 意味着 NT 口令可用于 连 接 SQL Server 系统<br>如果使 用 SQL Server 认证 那么 需要 使用 SQL Server 特定 ID 登录 到 SQL Server 中 单击<br>下一 步 则用 SQL Server 初始化连 接 并 且显 示 该连接 默认 的对 话框 如图 4-13 所示图 4-13 更改 SQL Server DSN 的默认数据库<br>该对话 框顶 部的 复选 框允许 设 置在 SQL Server 登录 中指 定的 默认 数据 库 在本 例中<br>可以看 到选 择 SQL Server 的示例数 据 库 pubs 作为默 认 数据库 对于其它 选 项连接 可以<br>使其他 项为 默认 值 单击 下一 步 按 钮则 显示 下一个配置 SQL Server DSN 对话框 如图<br>4-14 所示<br>图 4-14 设置语言 字符集和区域变量<br>该对话 框允 许设 置 SQL Server ODBC 驱动 程序 将要 使 用的语 言 字符 集和 区域 变量<br>对于大多 数用户的安装 默认的设置 是可接受的 单击 完成 则 完成数 据源 配 置 在这<br>之后 一 系列 可选 的对 话 框允许 测试 新创 建的 数据 源<br>在 SQL Server ODBC 驱 动 程序安 装和 使用 ODBC 管 理器 配 置数据源 之后 可以准 备<br>Access SQL Server<br>制作从 到 的连接<br>4.2.3 链接 表<br>Access 总是从打开一个本 地 的.mdb 文件 开始 .mdb 文件一 般位于本地的硬盘驱动器<br>上 但 是 它 也 可 以 位 于 一个网 络 共 享 的驱动器上 .mdb 文件包含了所有 Access 数据 库对象 例如 表 视图 查 询和报表 定义 正 如可 以从 一 个数据库系 统中 得到 的那 样 Access<br>的核心 数据 库组 件是 表 所有其 他数 据库 对象 都是 基 于在 Access 表 中定义 的数 据库 结构 创<br>建的 在 一 个 典型的单用 户实 现 中 Access 表包含组 成 数据 库 的 所 有 实 际 数据 但是 这 并<br>不是使 用 Access 作为 SQL Server 的前端那 种情 况 在 这 种模式 中 SQL Server 数 据 库包含<br>了 全 部表 和数 据 最初 这似 乎是 一个 问题 然而 Access 提供了 一 种 链 接表机 制 允许<br>本地的 Access 表使 用 ODBC 连接到远 程的 数 据库 表 全部 Access 的界 面 查 询 和报表 创<br>建组件都 可以使用链接 表 就好像它 们是本地表一样 在下 一节 中 将会 看到 如 何创建 一<br>组链接 表 这些 表链 接到在 SQL Server 的示 例数 据 库 pubs 中的 表<br>创建数 据库 是建 立 Access 数据库 应用 程序 的第 一步 建立一 个使 用 SQL Server 数据<br>库的 Access 应用 程序 没有什么 不 同 在图 4-15 中 可以 看到 新建 对话 框 它允许 Access<br>创建一 个新 数据库 单击 空数据 库 图标 然 后单击 确定 则 创 建一 个将 使用 链接 到<br>SQL Server 的表的 新数 据库<br>图 4-15 创建一个新的 Access 数据库<br>注意 为了 简单 起见 本 示例示 意创 建一 个新 的 Access 数据 库 它所包含的表将链接<br>到 SQL Server 的pubs 数 据库中的表 还可 以将链 接表添加到一 个 已 有的 Access<br>数据库 中<br>Access 显示 一个 对话 框 允许输 入将 要链 接到 SQL Server 数据库的 Access 数据库 的<br>Access .mdb<br>名称和 位置 在指 定数 据 库的名 称和 路径 之后 创 建一个 新的 文件 并且 显示<br>主 Acess 数据 库窗 口 如图 4-16 所示 在本 示例 中 Access 的 数据库名 称是 AccessPubs<br>正如所 期望 那样 该数 据 库存储 在 AccessPubs.mdb 文件 中图 4-16 创建一个新的 Access 表<br>在如图 4-16 所示 的主 Access 数据 库窗 口上 的新 建按 钮允 许创 建新 的 Access 数据库 对<br>象 第一 个标签允许创 建表 其他标 签允许创建新查 询 窗体 报表 宏和代码模块 为<br>了创建 一个 链接表 选择 表 标签 然后 单 击 新建 这时 显示 一个弹出对 话框 如<br>图 4-17 所示<br>图 4-17 选择 Access 表类型<br>这个对 话框 允许 选择 将要创 建 的 Access 表的类 型 当 设计一 个本 地的 Access 表时<br>一般选 择 数据 表视 图 设计 视图 或者 表向 导 来调 用 Access 的 本地数 据库 设计 实<br>用程 序 如果 希望从另 一个源 如 Excel 工作表 拷贝 表结构和数据 可以 选择 导入<br>选项 该选 项也 可以 用来从 一 个 ODBC 数据源 如 SQL Server 引入 数据 然而 在使 用<br>导入 选项和使 用 链接表 选项之间 有很大的不同 导入 选项在本 地 的 Access<br>数据库 中制 作一 个引 入数据 的 快照 拷贝 任何 以后 的 Access 操 作都只 影响 到该 数据 的本 地<br>拷贝 原始数 据源保持 不变 链接 表 选 项 创建一 个 ODBC 连接 该连接 将用于访 问存<br>储在远程 数据源的数据 没有执行数 据的本地拷贝 所有的 数据库操作都 在远程 数据库 表<br>中执行 为了 创建 一个 链 接到 SQL Server 表的 新 Access 表 从列 表中 选择 链接 表 然<br>后单击 确定 这时 显示 Access 链接 向导 如图 4-18 所示图 4-18 选择链接表源<br>该对话 框允 许选 择将 要用于 链 接表 的数 据源 为了 创 建一个 从 Access 表到 SQL Server<br>表的链 接 必须 单击 文件类 型下 拉 框 然后 从列 表框中 选 择 ODBC Database 就会出 现 ODBC<br>Driver Manager 的 Select Data Source 对话 框 如图 4-19 所示<br>图 4-19 选择 SQL Server 数据源<br>在这里 必须 选择 使用 SQL Server ODBC 驱动 程序 的 ODBC 数 据源名称 然 后 单击确<br>定 该 ODBC Driver Manager 将加 载 SQL Server ODBC 驱 动程序 并且 启用 一个 到 SQL<br>Server 的连接 如果 使用 混合 安全 模式 那么 ODBC Driver Manager 提 示输入一 个 SQL Server<br>登录帐 号 ODBC Driver Manager 将制作 一个 到 SQL Server 数据库 的连 接 该 数 据库是 在<br>创建数 据源 时 指 定的 然而 在登 录进 程 中 可 以忽略 这种 指 定 在 创 建了 到 SQL Server<br>的连接 之后 就列 出 SQL Server 数据库中 的表 清单 在图 4-20 中 可以 看到 pubs 数据库<br>中的 SQL Server 表清单图 4-20 选择要链接的 SQL Server 表<br>Access 总是 使用 所有 者前缀 区 分连 接表 因此 所 有 列出的 表由 前缀 dbo 开始 其后<br>是表名 通过 加亮 列表 中 的表 可以 选择 单个 表 或单击 Select All 可以 选择该 SQL Server<br>数据库 中的 全部 表<br>注意 当链 接到 SQL Server 上的文 件时 最 好逐 个 选择希 望链 接的 文件 选择全 部表<br>也包括 SQL Server 的系 统文 件 这些 不是 应用 程序文 件 他们 一般 不应 该包 含<br>在Access 数据 库中<br>链接一 个 Access 表和 一个 SQL Server 表 在本 地的 Access 数 据库中创 建了 一个 Access<br>表定义 在本 章的 这些 示 例中 链 接表是在 AccessPubs.mdb 数据 库中创建 链 接表定义 包<br>含该表 的结 构和 ODBC 驱 动程序 以及 登录 帐户 信息 这些 信息 是连 接 SQL Server 所需要的<br>有表 结构 的本 地拷贝允 许 Access 的设 计工 具当 创建 查 询 窗体和报表时提供非常好的应<br>答 然而 当链接表包 含所有表和列 信息的拷贝时 它并不 包含实际的数 据 基 表仍然 留<br>在 SQL Server 数据库中<br>不像 SQL Server Access 要求所 有可 修改 的表 都 有唯 一列标 识符 这是 由于 Jet 引擎<br>使用了一 种优化的记录 锁结构 在这 种结 构 中 根据 唯一列值进行 修改 之前 检 索要修 改<br>的每一行 然后 Jet 引擎检查这些列值 确保没 有其 他用 户 修改该 行 虽然 SQL Server<br>不要求 这样 但是 任何 由 Access 修改 的表 要求 如此 如果该 链接 表没 有唯 一列 标识 符 那<br>么该向 导显 示 Select Unique Record Identifier 对话框 如图 4-21 所示 该 对话 框 提示选 择一<br>个可用 作唯 一记 录标 识符的 列<br>图 4-21 选择唯一记录标识符可以在 SQL Server 表中选择 一个 列 使该 列用 作行 的唯 一标 识符 当 选择相 应的 列之<br>后 单击” 确定” 则创 建该表 的 链接 Access Table Wizard 进行下一 个 选 择的 表 也 可 以跳过<br>选择用 于唯 一索 引的 列 但是 如果 这样 做 那么 Access 将 把 这些表作 为只读表 不允许<br>对表进 行修 改<br>注意 在链 接到 SQL Server 表之前 确 保理 解了 将 要链接 的所 有表 的结 构 这不 仅是<br>创建数 据库 应用 程序 要求的 知 识 而且 还使 处理 Select Unique Row IDentifier<br>对话 框更加简 单 然而 如果在这种 进程 中产 生错 误 那么 总可 以从 Access<br>数据库 中删 除该 表 然 后 重新链 接该 表 从 Access 中 删除一个 链接 表并 不从 SQL<br>Server 中删 除该 表 它只 是删 除本 地的 链 接<br>在链接 所有 要求 的表 之后 这些 表将 出现 在 Access 表 的清单 中 如图 4-22 所示 在<br>Access 中 所有 链接 表都与 ODBC 全局 图标 有关 而不 是与 用于 本地 Access 表 的 小表图<br>标有关 在图 4-22 中 可 以看到 来自 pubs 数据 库的 全部 表都 已经 链接 到相 应的 Access 表<br>当要求 的全 部 SQL Server 表链接 之后 可以 开始 使用 Access 的界 面 工具 来建 立数 据输入窗<br>体 查询 和报 表 它 们使用 SQL Server 表中 的数 据 Access 提 供了一 个丰 富的 开发 环境<br>他们包括用于快速生成数据 输入窗体 定 制查询和报表的工具 以及 使用 VBA 为执行复<br>杂的编 程任 务的 工具 在本 章 下一节 将介 绍这 些基本 步 骤 这些 步骤 要求 使用每 一 个 Access<br>的主数 据访 问工 具和 SQL Server 表<br>图 4-22 链接表清单<br>4.3 设计 Access 应用 程序<br>Access 连接 到 SQL Server 之后 我们 就可 以访 问 SQL Server 的数据库 信息 了 本节主<br>要介绍 如何 用 Access 的 各 类设计 工具 快速 设计 出各种 常 见的 Access 应用 比如 查询<br>窗体 报表 宏等<br>4.3.1 查询 设计<br>Access 的 查询 设计 器允 许 使用其 图形 化查 询生 成器 界 面快速 创建 查询 就像 Access 的<br>其他数 据库 工具 一样 查 询设 计器可 以用 于链 接 SQL Server 表 就像 链接 Access 内部表一<br>样简单 Access 查 询可 以 用作独 立的 工具 或者 作为 Access 窗 体或者 报表 的基 础 为了 使用 Access 的查 询创 建器 创 建一个 新查 询 首先 从主 Access 对话 框 中选 择查询标签 对<br>于一个 新数 据库 初始 化 的 查询对话 框是 空的 如图 4-23 所示<br>图 4-23 Access 数据库的 Queries 标签<br>为了创 建一 个使 用链 接的 SQL Server 表的 新查 询 单击新建 这将启动 Access 的查<br>询向导 Query Wizard Access 查 询向 导允 许创 建几种不同类型的 查询 查 询 向导可 以启<br>动设计视 图 简单查询 向导 交叉表 查询 向 导 查找 重复项 查 询向 导 或 者 查 找 不匹配 项<br>查询向导 简单查询向 导指导 用户创建一个 没有行选 择的简 单查询 交叉表查询 向导指导<br>用户创建 一个交叉查询 它修改来自 两个不同表列的 结果 查找重复项和 查找不匹配项 创<br>建的查 询可 定位 那些 要 么是 两 个表 之间 的冗 余 值 要么 是 在两 个表 之 间 没有 匹配值的列值<br>设计 视 图 调 用 基 本 的图形 查 询设计器 它 可 以 用 于 基本的 查 询和复杂的查询 Access 设计<br>视图如 图 4-24 所示<br>图 4-24 Access Query Design View<br>通过提 供包 含在 Access 数 据库中 的全 部表 的清 单 启动 Access 设 计视图 然 后从列表<br>中选择将 要包含在查询 中的表 所选 的表出现在设计 视图的 上部 表示在所选表 之间任 何<br>关系的 连接 在图 4-24 中 看到 这个 示例 查询 包括 三 个表 authors titleauthors 和 titles<br>如果熟 悉 pubs 数据 库 那 么就知 道 authors 表包 含了 样本 作者 信息 titleauthors 表提供 了<br>从作者 到他 们写 的书 之间的 链 接 titles 表 包含 了有 关 每一本 书的 书名 信息<br>提示 对于 快速 设计 查询和 报 表来 说 像 Access 的 Query Design View 这 样强大的 工具 可 以大 大节 约时 间 但 是它 们 并不能取代基本数 据库结构 的知 识 无论 使 用<br>哪一种 工具 为了 有效 地 使用这种工 具 必 须理 解数据 库 表及其关 系<br>在表窗口中将 列拖放到该屏幕下部的设计 网格中 可以选择列 在这 个示 例 中 可以<br>看到已 经选 择 au_lname au_fname title price 和 state 列 包括在 该查 询中 除了 state 列之<br>外 所有列都将在结果集中可见 包括 state 列是为 了 允许基 于列 值“CA”选择行 这样<br>在查询中 只包括来自 加利福尼亚的 作者的那些行 单击感 叹号按钮执行 该查询 并且 显<br>示结果 窗口 如图 4-25 所示<br>图 4-25 查询结果窗口<br>使用 Access 查 询设 计的 结果清 楚地 说明 Access 可以 快速 而 轻 易生成结 果的 威力 在<br>本示 例 中 Access 查 询 设计 只 使用 几 个 交 互 式 动 作建 立了 一 个包括行 选择条件的 三个 文 件<br>的连接 生成 这个 结果 时 不需直接 输入 任何 SQL 语句 在这 种表 面现象之后 Access 创<br>建了所 有要 求的 SQL 语句 使用 右击 结果 窗口 时 显示 的弹出 菜单 允 许看到由 Access 生<br>成来产 生结 果的 SQL 语句<br>生成查询不是 一次性进程 使用右击结果 窗口时 显示的弹出菜单 可 以在结果窗口和<br>设计窗口 之间选择 快速查看查询结果 再 切换到 设 计模式 仔细 调整查询条件 的能力允<br>许快速 开发 复杂 的查 询<br>当完成设计查 询时 单击保存按钮则保存 查询说明 关闭查询设计视 图窗口也要提示<br>保存查 询说 明<br>4.3.2 窗体 设计<br>Access 的窗 体设 计器 允许使 用 类似 Visual Basic 的窗 体设 计器 或者 通过 一组 向导 快速<br>地创建 简单 的数 据输 入窗体 像其 他的 Access 数据 库 工具一 样 窗体 设计 器可 以用 于链 接<br>的 SQL Server 表 就像 用于 Access 本地 表一 样 为 了启动 Access 窗 体设计 器 从 Access 数<br>据库窗 口中 单击 窗体 标签 然后 单击 新建 这时 显 示新建 窗体对话 框 如图 4-26 所示<br>该对话 框提 供了 用于 创建数 据输 入窗 体 的几 个选 项 设 计视图 选 项启动一 个类 似 Visual<br>Basic 的图 形化 窗体 设计 器 适合 于设 计访 问一 个或 者多 个表 的定 制 窗体 窗体向 导 创建一<br>个基于一个表或者查询 的简单窗体 自动窗体有纵栏表 表格或者数据表等选 项 顾名 思义 纵栏 表布局一次提供 一行 数据 显示 在两 列 上 表格 布局 显示多行多 列 而数据表布<br>局在绑定 数据的网格上 显示多行 图 表向导创建可显 示几种 图形图表的窗 体 数据透 视表<br>向导创 建一 个包 含一 个 Excel 数据透 视 表的窗 体 选 择 窗 体向导 则 显示如图 4-27 所示的 窗<br>口<br>图 4-26 新建 Form 对话框<br>图 4-27 窗体向导窗口<br>窗体向 导很 类似 于查 询 向导 提 供的 那样 也 允许 你选 择 包 含在该 窗体 中 的 多表和 多列<br>在本示 例中 可以 看到 这 种特殊 的窗 体基 于 department 表 并且 包含 在 department 文件中<br>的两个 列已 经包 含在 窗体中<br>注意 Access 的数 据输 入窗 体操 作 SQL Server 表中 的数据 使用 这些 窗体进行的 任<br>何改变 都写 到 SQL Server 数据 库中 为了 使 pubs 数 据 库中的这 些表 保持 其原<br>始状态 本示 例使 用一 个 用户创 建的 表department Department 表 不是原始pubs<br>数据库中的一部分 创建它的目 的是说明窗体 的输入和修改而不必修改原始的<br>pubs 表 Department 表有 两个 列 Dep_ID 是整数 数 据类型 它也 是该 表的 唯<br>一键 Dep_Name 是一 个长 25 的字符 类型 列当选择 包含 在窗 体中 的表和 列 之后 单 击完 成生 成数据输入窗体 如下所 示<br>该数据 输入 窗体 说明 了 Access 快速生 成可 以使 用 SQL Server 表的数据输入窗体的 能<br>力 这种 数据输入窗体 只用两分钟就能生成 并且 不 要求编 码 该窗体允 许用户 输入 修<br>改或者 删除 department 表中 的行 在窗 口底 端的 数据浏 览 栏允 许在 SQL Server 表 中 向前向<br>后滚动 行<br>注意 Access 的数据 输入 窗体 和 动作查询 可以 执行 终 端用户有 数 据库许可执行 的动<br>作 必须 在允许终端用 户访问存储在 SQL Server 数据库中的公司数 据之前<br>实现安 全性 数据 库访 问规划<br>4.3.3 报表 设计<br>Access Access<br>报 表设 计器 使用 图 形化的 报表 设计 器界 面或 者 选择其 中一 个 的报表向<br>导 允许 快速 地创 建报 表 像 Access 的其 他数 据库 工 具一样 报 表 设计器 使用 链接 的 SQL<br>Server 表 就像 使用 内部 的 Access 表一 样 当设 计报 表时 可以 在报表 本身 上包 含表 和行<br>选择条件 或者可以使 该报表基于一 个已经存在的查 询 在下一节 我们 将看 到 如何使 用<br>一个预 定义 的 Access 查 询 创建一 个新 的 Access 报表<br>Access Access <br>不像 查询 设计 器 它是自动确定表 之间的关系 报 表设计器 不能 确 定<br>不同的表之间 的关系—— 即使是列名 相同也是如此 然而 像窗体设 计器一样 报表设 计<br>器能够使 用一个已 存在的查询作为输 入 这样就允许 设计的查询也可以把双层条 件作为 报<br>表选择 条件 在图 4-28 中 可以 看到 显示 在查 询设 计 器中的 SQL Server 的 pubs 数据库 中<br>三个表 的样 本查 询<br>图 4-28 报表输入的样本查询如上所 示 该 样本 查询 创 建了一 个结 果集 根据库 存列出 销 售情况 stores 表通过 stor_id<br>列链接 到 sales 表 并且 为了 得到 该书 名的 信息 该查 询还 使用 title_id 列连 接 sales 表和<br>titles 表 然后 使用 Sales 名称 根据 Stores 保 存该查 询 当定 义该 报 表条件 的 查 询定义<br>之后 可以 使用 该查 询作为 在 Access 报表 设计 器的 输 入<br>为了使 用 Access 的 报表 设 计器创 建一 个新 报表 首先从 主 Access 数 据库对 话框 中选<br>择报表标 签 刚开始 对于一个新数 据库 报 表对 话 框将是 空的 为了创 建一个 新报表<br>单击新 建 则显 示如 图 4-29 所示 的新 建报 表对 话框<br>图 4-29 新建 报表对话框<br>报表向导 选项允许启动 一个可视 的报表设计器 允许 手工布局报表 报表向导指导 用<br>户通过一 系列对话 框 帮助创建有几 个列的报表 自 动报表选 项允许创建使用纵 栏表布 局<br>或者表格 布局的报表 纵栏表 布局提 供一次一行 有 两个列 的数据输出 表格布 局在 一个<br>单页面上 显示多行 和多列 图表向导 创建一个可以显 示几种图形的报表 标签向导用于 打<br>印几种 不同 类型 的邮 寄标签<br>创建新 报表 的最 简单 方法是 使 用 Access 的报表 向导 在图 4-29 中 在上面 的列 表中<br>选择报表 向导 在下拉 列表框中选择 以前创建的查询 名称 单击确定 显 示下 一 个报表 向<br>导对话 框 如图 4-30 所示<br>图 4-30 报表向导列选项对话框在上面的下拉 列表框 报表向导对话框显 示要作为输入的查询名称 最初 包括在查<br>询中的全 部列都将列在 该对话框左端 的列表框中 并 且该对 话框右端的列 表框是 空的 可<br>以单击左 端列表中 的每一个列名 然 后单击右箭头按 钮 把该列增加到右端列表 选择 包<br>含在查询 中的字段 同样 可以在右 端列表中选择将 要删除 的列名 然后 单击 左 箭头 从<br>报表中 删除 该列 当选 择 了所有 希望 的列 之后 单 击 完成按 钮则 生成 如图 4-31 所 示 的报表<br>图 4-31 一个 Access 报表<br>该报 表进 一步 说明 了 Access 的快速数 据库 开发 能 力 使用一个预创建的查询作为输<br>Access<br>入 这个 报 表执 行 了一个 三文 件连 接 并 且在几 分 钟内创建 用户可能会 看 到 尽<br>管它可以 快速而简单地 创建报表 但 对于挑选报表和 列标题 它并不能做 得很好 显然<br>使用 SQL Server 的表和 列标 题并 不是 最友 好的 报表 布局 然而 一旦 报表生成之 后 就可<br>以快速 改变 报表 的标 题 为了定 制该 报表 选择 Access Database 对 话框上的 报 表标签 然<br>后单击 报表 选择 希望 定 制的报 表 当加 亮希 望的 报 表之后 可以 单击设计来调用 Access 报<br>4-32<br>表设计 器 如图 所示<br>图 4-32 使用报表设计器定制报表<br>所选的报表将 会显示在报表设计器中 从 报表设计器中 可以 单击 希望修改的字段<br>然后输入 新的标题 值 改变该报表的 标题和列标题 同样 将 字段从一个位置移 动到另 一<br>个位置 可以 改变 报表 的 布局<br>提示 图 形化的报表设 计器是一 种易于使用的 通用工 具 即使 如此 一般最好使用 相应的向 导生 成初 始的 报表 然后使用 报表 设计 器调 整 报表<br>4.3.4 宏设 计<br>Access 宏 允许 自动 重复 操 作或者 组合 相关 操作 的批 例如 可以 使用 Access 宏运行 一<br>系列的 查询 或者 报表 显 示预先 确定 的数 据 输入 窗体 的顺序 或者 拷贝 和更 名 Access 数据<br>库中的 全部 表 宏不 能提供 由 VBA(Visual Basic for Application) 代码提 供的 更细的 控 制 但<br>是它们仍然是一种强大的自动化工具 为了创建一个新的 Access 宏 必须先在 Access<br>Database 对话框中的宏标签上选择一个宏 然后 单击 新建 这时 显示 宏的 编辑 器 如图<br>4-33 所示<br>图 4-33 运行一批报表的 Access 宏<br>在 宏 编辑 器中 可 以看到 一 个宏 QueryBatch 该 宏 组合了 两 个 查 询和一个报表 操作<br>列控制指 定的动作 这 些动作的每一 步都在宏中执行 备 注 列 是 简 单地描 述将 要 执行的 动<br>作说明 用户 可能 会猜 测 OpenQuery 动作运行一 个已有的查 询 而 OpenReport 动作运 行<br>一个已有 的报表 在该 窗口下半部分 的文本字段中指 定该动 作将要应用到 的数据 库对象<br>在图 1-33 中 可以 看到 OpenReport 动作 将打 开保 存的 报表 Sales by Stores 视图字段表 示<br>该输出 将定 向到 打印 机<br>除了 Open Query 和 OpenReport 外 Access 还支持 许 多动作 表 4-2 列出 了由 Access<br>支持的 不同 的宏 动作<br>表 4-2 Access 宏命令<br>动作 描述<br>创建一个定制菜单<br>AddMenu<br>限制数据<br>ApplyFilter<br>发出嘟嘟声<br>Beep<br>取消引起宏运行的事件<br>CancelEvent<br>关闭指定的窗口<br>Close<br>拷贝指定的对象<br>CopyObject<br>删除指定的对象<br>DeleteObject<br>在屏幕上显示文本<br>Echo 续表<br>动作 描述<br>找到匹配 FindRecord 条件的下一条记录<br>FindNext<br>查找匹配搜索条件的第一条记录<br>FindRecord<br>集点设置在窗体的指定控件上<br>GoToControl<br>在活动的窗体上移到指定的页面上<br>GoToPage<br>移动到指定的记录<br>GoToRecord<br>鼠标指针切换<br>Hourglass<br>最大化活动窗口<br>Maximize<br>最小化活动窗口<br>Minimize<br>移动和调整活动窗口<br>MoveSize<br>显示一个消息框<br>MsgBox<br>显示指定的窗体<br>OpenForm<br>打开指定的代码模块<br>OpenModule<br>运行指定的查询<br>OpenQuery<br>运行指定的报表<br>OpenReport<br>打开指定的表<br>OpenTable<br>引出数据库对象的内容<br>Output To<br>打印数据库对象的内容<br>PrintOut<br>退出 Access<br>Quit<br>重新命名指定的数据库对象<br>Rename<br>刷新指定的屏幕对象<br>RepaintObject<br>刷新一个查询<br>Requery<br>当前窗口恢复为其前一个窗口<br>Restore<br>执行 Windows 应用程序<br>RunApp<br>执行 VBA 函数<br>RunCode<br>执行 Access 菜单命令<br>RunCommand<br>执行 Access 宏<br>RunMacro<br>执行 SQL 语句<br>Run SQL<br>保存指定的对象<br>Save<br>选择指定的对象<br>SelectObject<br>击键<br>SendKeys<br>通过电子邮件发送一个对象<br>SendObject<br>设置菜单项的状态<br>SetMenuItem<br>设置控件或者数据库字段的值<br>SetValue<br>切换系统消息的显示<br>SetWarnings<br>删除全部过滤器<br>ShowAllRecords<br>显示指定的工具栏<br>ShowToolbar<br>终止所有宏的执行<br>StopAllMacros<br>终止当前宏<br>StopMacros<br>从数据库中引入引出数据<br>TransferDatabase<br>从电子表格中引入引出数据<br>TransferSpreadsheet<br>引入引出一个 ASCII 文本文件<br>TransferText可以看 出 Access 宏可 以 快速构 造 并且 提供 了 各种 功 能模块 可以 使用 宏模 拟 VBA<br>应用程 序 可以 保存 宏作为 Visual Basic 的模 块 为 了保存 一个 已经 存在 的宏 作 为 Visual Basic<br>模块 右 击宏名称 然 后从弹出的菜单中 选 择 另 存 为/导出选项 这时 显示该 宏的另 存 为<br>对话框 如图 4-34 所示 选择 另存为 Visual Basic 模块单选 按 钮 然后 单击确定 把该 宏<br>转变成 VBA 代码<br>图 4-34 宏保存为 Visual Basic 模块<br>4.4 开发 VBA 应用 程 序<br>宏是一个自动化常用任务和模拟应用程序的很好工具 它没 有提 供与 VBA 代码相同<br>层次的 应用 程序 控件 VBA 是一种封闭 的 Visual Basic 编 程语言 这种 Access 实 现 提供了<br>许多 Visual Basic 有 的高 效率工 具 例如 像 Visual Basic 一样 Access Module 编 辑 器支持<br>彩色代 码关 键字 语 句完成 语法 检查 和集 成调 试 VBA 非 常适 合 于 复杂的任 务 还可 以<br>Access VBA<br>用于操 纵 之外的其他对 象 例如 标 准 的操作系统文 件 另外 代 码 提供了 一<br>层在 Access 模块 中没 有的错 误 处理<br>Access 提供 了 DAO( 数 据 访问对 象)对象框 架作 为 Jet 引擎 的 一 种编程 界面 可以 使用<br>VBA 和 DAO 创建 数据 库 应用程 序 这 些应 用程序 可以 使用 本地 的 Access 数 据 库或者 SQL<br>Server 数据库<br>VBA Access SQL<br>本章的 下一 节介 绍一 些常见 的 编码 技术 可以使 用 这些技术在 中访问<br>Server 数据库 第一 个示 例说 明如 何使 用 DAO 执行 一个 简单 的查 询 第二 个示 例说 明如 何<br>建立和 执行 参数 化查 询 第三个 示例 说明 如何 用 SQL Passthrough 调用 SQL Server 的存储<br>过程 使用 SQL Passthrough<br>为了创 建一 个新 的 Access 代码模 块 从主 Access Database 对 话框中 选择 Module 标签<br>Access VBA<br>然后单 击新 建 这时 显示 代码 编辑 器 当代 码 编辑器 显示 时 创建 函数或 者<br>子例程 执行 定制 的数 据 库访问<br>提示 当在Access 中写VBA 代码时 最好 创建 函数而 不 是子 例程——即 使该代 码模 块<br>不需要 返回 任何 值 对于 Access Macro Builder 或者 Switch board Add-in<br>VBA 子例 程是 不可 见的 而函数 可见<br>4.4.1 使用 DAO 记录 集<br>DAO 记录 集对 象表 示一 个 查询的 结果 使用 DAO Database 对象 的 OpenRecordset 方法创建 Recordset 对象 正如 所希 望的 那样 DAO Database 对 象表示 一个 Access 数据库<br>下面的 DAOQuery 函数说明 如何 从当 前数 据库 中创 建一 个新 记录 集 然 后访 问 包含在 该记<br>录集中 的全 部行 和列<br>Private Function DAOQuery()<br> Dim db As Database<br> Dim rs As Recordset<br> Dim fld As Field<br> Set db = CurrentDb<br> Set rs = db. OpenRecordset("Select From dbo_authors",<br> dbOpenDynaset)<br> Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> rs.MoveNext<br> Loop<br> rs.Close<br>End Function<br>在这 个子 例程的开始 声明了几个 DAO 对象类型变 量 用于 存 放 DAO Database<br>Recordset 和 Field 对象 接下 来 把当 前数 据库 的值赋 给 db Database 对 象变量 CurrentDB<br>对象是 一个 表示 当前 数据库 的 Access 对象 在本 示例中 变量 db 是 AccessPubs.mdb 数据<br>库的一 个实 例 它包 含了链接 到 SQL Server 的 pubs 数 据库的 表集<br>接下 来 使用 Database 对象上 的 OpenRecordset 方法创建一 个新的记录集 对象 rs<br>OpenRecordset 方 法带 两个参 数 第一 个参 数是 一条 SQL 语句 它定 义将 要返 回 的结果 集;<br>第二个 参数 是一 个常 量 它指定 将要 创建 的 Recordset 的类 型 在本例 中 SQL Select 语句<br>从表 dbo_authors 中检 索全 部行 和列 该表 链接 到 SQL Server pubs 数据库 中的 authors 表<br>常量 dbOpenDynaset 表示这将 是 Dynaset 类型 的 Recordset 它支持修 改以 及 前后 滚动 执<br>行 OpenRecordset 函数 发送 SQL 请求 到 SQL Server 然后 返回 满足 该请 求的 结果 集<br>使用 Do Until 循环 可以 访问 记录 集对 象的 内容 在这 个循 环内 执行 rs Recordset 对象<br>的 MoveNext 方法 在 Recordset 对象内向前移动当前游标的位置 该循环一直执行到<br>Recordset 对象 的 EOF 属性为 真为 止 这就 表示 读完了 Recordset 中 的所有 行 在 Do Until<br>循环内 使用 For Each 循 环处理 每一 行的 Field 对象 在 DAO 中 Field 对象 表示一个列<br>当在 记录集对 象中的全 部行和列 都 读 完之 后 调用 Close 方法释放记录 集对象 并且 重新<br>声明它 使用 的资 源<br>4.4.2 使用 带有 参数 的查 询<br>前面的 DAO 示例 说明 使用一 个 动态 SQL 语句 来创 建一 个 Recordset 对象 构 造 和执行<br>动态的 SQL 语句是非 常简单 的 当每 一次 执行 这些 SQL 语句 时 必须 对这 些语 句进 行语法分析 并创 建执 行规 划 这对于 ad hoc 查询类 型的 应 用程序 很好 因为它们很少重 复 相 同<br>的语句 但是 对于在线事务处 理(OLTP) 类型的应用程序 就不是最好的方法 因为它多次<br>执行同 一条 语句 使用 预 先准备 的 SQL 语句 OLTP 类 型的应用 程序 可以 得到 最 好的性 能<br>不像动 态的 SQL 语句 每 一次运 行这 些语 句时 都 必 须创建 新的 执行 规划 而使 用预 先准<br>备的 SQL 语句 只 创建 一 次执行 规划 并且 是在 准备该 语 句时 创建 所有 以后 的执 行使 用<br>已有的 访问 规划 不需 要 创建新 的执 行规 划 这就 使 重复执 行预 先准 备的 SQL 语 句 比动态<br>的 SQL 语句的性 能有 了极大 的 提高<br>如果每 一次 执行 准备 好的 SQL 语句 时都 需要维 护 完全相 同 的 SQL 语 句和选 择标 准<br>那么它并不非常灵活 对于应 用 程 序需要执行的每一次查 询 都 需 要一 条完 全 不 同的 SQL<br>语句 幸运 地是 预 准备的 SQL 语 句能 使用参 数标 记 它 实际上是数 据值 的占 位符 参数<br>允许用 不同 的数 值重 新使用 同 一条 SQL 语句<br>Access 通过 Query Definition 对象支持 参数化查 询 使用 VBA 代 码或使 用查 询设 计器 创建<br>查询 可以 在 Access 中创建 一 个参 数化查 询 在图 4-35 中 可以 看到 使用 一个 输入 参数<br>的 Access Query Definition<br>图 4-35 使用查询设计器创建参数化查询<br>SQL Server Stores Sales titles<br>这个查 询定 义执 行了 三个 文件 的连 接 和 表 这 种特殊<br>的查询从 输入文件中检 索商店名称 商店状态 书 名 和销售 数量 列 不像 本章 前 面提出 的<br>比较简 单的 查询 在执 行 该查询 之前 该查 询提 示匹配 状 态列 的值 在条件行中的 Enter the<br>State 子句 告诉 Access 在运行 该查 询之 前 提示 该 列的值 该 Access 查 询定义保 存在 State<br>的 Sales 下 如果 直接 从 Access 运行 该查 询 那么 出 现一个 对话 框 提示 输入 一 个 state 代<br>QueryParm VBA DAO<br>码 下面 的 函数说明 如何 使用 和 执行 一 个参 数化查询<br>Function QueryParm()<br> Dim db As Database<br> Dim qd As QueryDef<br> Dim rs As Recordset<br> Dim fld As Field<br> Dim sStateCode As String Set db = CurrentDb<br> sStateCode = InputBox("Enter the state code")<br> Set qd = db.QueryDefs("Sales for State")<br> qd.! [Enter the State]= sStateCode<br> Set rs = qd.OpenRecordset()<br> Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> rs.MoveNext<br> Loop<br> rs.Close<br>End Function<br>像前面 的 DAO 示例 一样 这个函 数开 始声 明用 于 DAO 对 象的变量 以及 将要 包含 用<br>户提供 的 state 代码 的字 符 串变量 使用 CurrentDb 对象 db Database 对 象赋值 当前 的数 据<br>库 然后 一个 简单 的 InputBox 用来从用 户获 得 state 代码 该代 码存 储在 sStateCode 字<br>符串中 然后 qb QueryDef 对象 设置 为在 前面 使用查 询 设计 器创 建的 查询 定义名 称<br>接下来 这个 包含 在 sStateCode 变量 中的 输入 state 代 码赋值 给该 查询 的输 入参 数 该<br>查询的 输入 参数 使用 Enter the State 名称( 包 括 方括号) 它与在查 询设 计器 中 建立的 输入<br>参数同名 在赋值 输入参数之后 通过 OpenRecordset 函数执行该查询 然后 由该查询<br>返回的 结果 集内 容显 示在 Access Debug 窗口 中<br>4.4.3 使用 SQL Server 存储过 程<br>使用参 数化 查询 可以 显著提 高 需要 反复 执行 SQL 语句的 Access 应 用程序的 性能 然<br>而 可 以使 用存 储过 程获得从 Access 到 SQL Server 应用 程序的 最好 性能 非常 像预 准备 的<br>SQL 语句 存 储 过 程 的执 行 规划在存储 过 程 执 行 之 前创 建 然而 应用 程序 每运 行一 次<br>预准备的 语句必须准备 一次 而存储 过程的执行规划 只准备 一次 且 在 存 储过程 创建时 准<br>备 因为 没有 必要 分析 到 来的 SQL 语句 的语 法或 者准备 一 个执 行规 划 所以 存储 过程 为访<br>问 SQL Server 数据库提 供了 最高 的性 能机 制<br>SQL Server 存储过程还能够在存储过程中执行多个操作( 称为批) 并且可能 返回 多个<br>结果集 下面 的 CallSP 函数 说明 如何 使用 Access VBA 和 DAO 来调用一个 返回 多个 结果<br>集的 SQL Server 存储过 程<br>Function CallSP()<br> Dim db As Database<br> Dim qd As QueryDef<br> Dim rs As Recordset<br> Dim fld As Field Set db = CurrentDb<br> On Error Resume Next<br> db.QueryDefs.Delete ("Pubs_Report_1")<br> On Error GoTo ErrorHandler<br> Set qd = db.CreateQueryDef("Pubs_Report_1")<br> qd.Connect = "ODBC;DSN=TECA SQL Server;UID=sa;PWD=;DATABASE=pubs"<br> qd.ReturnsRecords = True<br> qd.SQL = "reptq1"<br> <br> Set rs = qd.OpenRecordset()<br> On Error Resume Next<br> db.TableDefs.Delete "TempResults"<br> db.TableDefs.Delete "TempResults1"<br> db.TableDefs.Delete "TempResults2"<br> db.TableDefs.Delete "TempResults3"<br> db.TableDefs.Delete "TempResults4"<br> db.TableDefs.Delete "TempResults5"<br> db.TableDefs.Delete "TempResults6"<br> On Error GoTo ErrorHandler<br> ''Now process the next result set using a Jet table<br> Set qd = db.CreateQueryDef(" ")<br> qd.SQL = "Select Pubs_Report_1. Into TempResults<br> From Pubs_Report_1"<br> qd.Execute<br> Set rs = db.OpenRecordset("TempResults")<br> Debug.Print "Recordset 1"<br> Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults1")<br>Debug.Print "Recordset 2"<br>Do Until rs.EOF<br> For Each fld In rs.Fields Debug.Print fld<br> Next<br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults2")<br>Debug.Print "Recordset 3"<br>Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults3")<br>Debug.Print "Recordset 4"<br>Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> <br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults4")<br>Debug.Print "Recordset 5"<br>Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults5")<br>Debug.Print "Recordset 6"<br>Do Until rs.EOF<br> For Each fld In rs.Fields Debug.Print fld<br> Next<br> rs.MoveNext<br>Loop<br>Set rs = db.OpenRecordset("TempResults6")<br>Debug.Print "Recordset 7"<br>Do Until rs.EOF<br> For Each fld In rs.Fields<br> Debug.Print fld<br> Next<br> <br> rs.MoveNext<br> Loop<br> qd.Close<br> rs.Close<br> Exit Function<br> ErrorHandler:<br> DisplayDAOError<br>End Function<br>CallSP 函数 开始 声明 了对 象变 量 用于 DAO Database Recordset QueryDef 和 Field<br>对象 然后 使用 CurrentDb 对象把 当前 的数 据库 赋 予 db Database 对象<br>接下来 使用 On Error 语句 激活 VBA 的错 误处 理 在本 示例 中 如果 后面 的 QueryDelete<br>方法失 败 On Error Resume Next 语句 用来 允许 进程继 续 进行 QueryDelete 方法用 来删 除<br>任何存 在的 查询 定义 Pubs_Report_1 的实例 删 除 任 何旧的 查询 定义 允许 CallSP 函数不 碰<br>到错误 继续 运行<br>接下来 dbDatabase 对象的 CreateQueryDef 方法用来创建一个新的查询定义<br>Pubs_Report_1 因为 这是 一个 新的 数据 库对 象 所以 SQL Server 连接 信息 必须赋 于 Query<br>Definition 的 Connect 属性 然后 ReturnRecords 的 属性设 置为 True 通知 DAO 该 Duery<br>Definition 将返 回一 个结 果集 接下 来 SQL 属性设置 为 reptq1 这是 pubs 数 据 库中已有<br>的存储 过程 的名 称 下 面 的程序 清单 列出 了构 成 reptq1 存 储过程 的 Transact-SQL 语句<br>Create Procedure reptq1 As<br>Select pub_id, title_id, price, pubdate<br>From titles<br>Where price is Not Null<br>Order By pub_id<br>Compute Avg(price) By pub_id<br>Compute Avg(price)<br>当在 pubs 数据 库上 运行 时 Compute 语句中 的 By 子句使该存储过程生 成七 个结 果集<br>为了 处 理 多 个 结果集 Access 必须 创建 多个 临时表 每 一个表 保存 一个 结果集 代码 的 下<br>一段使 用 TableDef.Delete 方法 删除 任何 存在 的旧 临时 工作 表<br>接下来 创建 一个 新的 QueryDef 它使 用 Pubs_Report_1 查 询作为 输入 然 后输出多<br>个临 时表 每 一个 临时 表 包含一 个不 同的 结果 集 该 DAO 对 象框架自 动 计 数 每 一个不 同<br>的结果 集 在本 示例 中 第一个 结果 集是 TempResults 第二 个结 果集 是 TempResults1 等<br>等 当运 行 Query Definition 的 Execute 方法时 处 理 多个结 果集 之后 每一 个 不同的 结<br>果集内 容用 前面 提供 的相同 技 术输 出到 Access Debug 窗口 中<br>4.4.4 错误 处理<br>在 CallSP 函数 和其 他 VBA 函数之间 一个明 显的 不同 点是 前者 使用 了错 误处 理 使<br>用 On Error 语句 允许 Access 捕捉 和回 应 运行时 的错 误 否则 它将 生成 应用 程序 错误<br>前面的 CallSP 函 数使 用两种 不同 的方 式处 理错 误 首先 On Error Resume Next 语句<br>用于知道 错误可能发生 的位置 在碰 到错 误 时 程序 继续执 行 而不中断 下一个 语句的 执<br>行 另外 On Error Goto ErrorHandler 语句 用来 捕捉 和回 应未 知的 错误 未知 的错 误是不<br>能预料的 因此 如果 碰到一个出乎 意料的错误 那 么程序 的执行不应该 正常继 续进 行<br>在这种情况下 该错误句柄执行 DisplayDAOError 函数 通知用户碰到了错误 子例 程<br>DisplayDAOError 如下 所示<br>Private Sub DisplayDAOError()<br> Dim er As Error<br> For Each er In Errors<br> MsgBox "Number: " & er.Number & vbCrLf & _<br> "Source; " & er.Source " vbCrLf & _<br> "Text: " & er.Description<br> Next<br>End Sub<br>DisplayDAOError 子例 程开 始时 声明 一个 包含 DAO Error 对 象的变量 接下 来 For Each<br>循环用 来在 DAO Errors 集 合中叠 代 循环 是必 要的 因为 Errors 集 合可以 包含 多个 错误 成<br>员 其中 每一 个成 员保 存 不同错 误条 件的 信息 在 For Each 循环 内 对于 每一 个显 示有 关<br>错误条 件的 基本 信息 创 建一个 Msgbox 在本 示例 中 显示 错误 信息 号 源代码行和文 本<br>描述<br>4.5 编 程 心 得<br>对于使 用 Access 执行 的任何 客 户机 服务 器类 型的 数 据库 应 用程序开 发 可以 应用 几<br>个常见 的提 示 当使 用 Access 连 接 到某 个外 部数 据库中的表时 遵循 下面 一些 建议<br> 使用链 接表 而不 是 OpenDatabase 方法 链 接表 在本 地 存储数 据库 结构 允许 Access<br>避免每 一次 打开 表时 从表中 检 索<br> 确信只 检索 需要 的数 据 本地的 Access 数据库 应用 程 序经常 检索 比需 要多 得多 的<br>数据 然 后执 行外 部的 函 数来 查 找到真正 需要 的数 据 可以使用 SQL Select 语句<br>的 Where 子句 限制从 SQL Server 中返回的 数据 大大 降低 服务 器 上 所需的 磁 盘<br>I O 以及 发送 结果 到 Access 所需 的网络 带宽<br> 尽可能地 使用只向前的 记录集 至今 为止 只向前的记录集是最 好的记录集执 行<br>类型 它们 还使 用比 较少的 本 地资 源<br> 只是当应用程 序确实需要修改记录集的能力时 才使 用可修 改的记录集 可修 改<br>的记录 集功 能更 强 但 是 也需要 比只 读的 记录 集更 多 的资源<br> 尽可能 地使 用存 储过 程 SQL Server 存储过 程为 访问存 储 在 SQL Server 中的 数据<br>提供了 最快 的速 度<br> 避免多 机种 环境 的连 接 多机种 连接 是 Access 和 DAO 的 功能之一 因为 它们允<br>许 Access 和其 他数 据源 中 的表连 接 然而 这几 乎总是需要创建一个 或者 多个 临<br>时表 它还要求本地的 Jet 引擎 执行 实际 的连 接 它 把工作 负荷 从高 端的 服务 器<br>移动到 通常 低端 的客 户机<br>Access 开发 应用 程序 的强大 工 具 可以快 速地 创建 连 接到 SQL Server 数据库 的客户机<br>服 务 器 数 据 库应用程序 然而 尽 管 其 生产效率很 高 Access 还是局 限 于一定 的 范围 和<br>灵活性 为了 完全 控制 定 制的应 用程 序开 发 需 要研究专门 的 应用程序开 发环 境 例如 Visual<br>Basic 和 Visual C++ 在后 面的 章节 中将 陆续 介绍 有关 方面 的 内容 另外 在 SQL Server 与<br>Access 的集 成方 面还 涉及到 数 据转 换问 题 因为 SQL Server 是一种 比 Access 更为强 大<br>灵活的 数据 库工 具 使用 Access 开发 SQL Server 数据库 应用程序有时并不能完 全满 足要 求<br>因此就 需要 将 Access 数 据 转换成 SQL Server 数据 然 后 直接用 SQL Server 进行开 发 SQL<br>Server 2000 中的 DTS 使得 这一 工作 非常 容易 但 具 体内容 不属 于本 章讨 论的 范 围第 5 章 使用 ODBC 访问 SQL Server 数据 库<br>ODBC 是 由微软 定义的一 种数据库 访 问 标准 提供 了一 种标 准的 桌 面数据库 访问 方法<br>以访问 不同 平桌 台上 的 SQL 数据库 本 章将介 绍如 何在 Visual Basic 中使 用 ODBC 应用程<br>序编程 接口(API) 开发 SQL Server 数据库 应用 程序<br>5. 1 ODBC 概述<br>ODBC 是微软公 司 给出的访问数据库 的 API 已 经 成为 事实上的数据库 访 问接口的工<br>业标准 ODBC 虽然 可以 使用 一个 ODBC 应用程 序访 问在 本地 PC 数 据库上的数 据 但是<br>它主要 用于 访问 在多 机种平 台 上的 数据 库 例如 SQL Server Oracle 或者 DB2 虽然 ODBC<br>最初是 作为 一种 Windows 标准 但是 它也 在其 他几 种平 台上 实现 这些 平台 包 括 OS/2 和<br>UNIX ODBC API 是独立于数据库的 并且基于由 X/Open 和 ISO/IEC 开发的<br>SQL/CLI(SQL/Call Level Interface) 规范 ODBC 3.0 标准由 76 个 函数组成 这 些 函数在<br>Microsoft 的 ODBC3.0 参考 与 SDK 指南中 表面 上 ODBC API 由一 组函 数调用 组 成 但<br>ODBC 的核 心是 SQL ODBC 函数的 主要 功能 是将 SQL 语句发送到目 标数据库 中 然后<br>处理这 些 SQL 语句产 生的结 果 显然 服务 器必 须支持 SQL 以 便通过 ODBC 来访问<br>5.1.1 特点<br>ODBC 是通 过一组 标准 的函 数调 用来 实现 的 ODBC 的 一 个最大的优 点是 它是 一种<br>被广泛 地采 纳的 桌面 标准 使用 ODBC 时 没 必 要理解 这些 函数 使用 ODBC 所需的所有<br>代码都 创建 在支 持 ODBC 的 应用程序 中 像 Microsoft Access Word Excel 或者 Visual Basic<br>对于桌 面应 用程 序 ODBC 已经成 为一 种广 泛采 纳的 数据 库访 问标 准 并且 有 180 多种桌<br>面应 用程序支 持 ODBC 用户只需 要知道如 何使用这 些 应 用程 序 应用程序的基层已经 编<br>码以使 用 ODBC API 这 种功能 应用 到终 端用 户应 用 程序中 像 Word 或者 Excel 以及许多<br>ODBC 的客 户机/服务 器开 发工 具 不 必 真正理解 ODBC API 就可 以写 基于 ODBC 的客户<br>机/ 服务 器应 用程 序 例如 Visual Basic PowerBuilder Delphi 和 Visual C++ 都提供了它<br>们自己 的代 码层 来封 装 ODBC 函数——使程序开发 人 员访 问 ODBC 功能 而不需直接理 解<br>ODBC 函数<br>5.1.2 通信 机制<br>就像 大 多 数技术一 样 通 过 由各 种 客 户机/ 服 务 开发工 具提供的 内 置 数据访问 函数 越<br>是较好 地理 解应 用程 序的深 层 次 越能更 加 有效 地使用 ODBC 由于 ODBC 主 要 是用于连<br>接 PC 到不 同的 客户 机/服 务器数 据库 系统 所 以它 一 般使用 了许 多网 络组 件 图 5-1 示意<br>了由 ODBC 连接 使用 的通讯 层 在顶 层 可以 看到 支 持 ODBC 的 应用程 序 它调 用由 ODBC<br>CLI 提供的各 种 ODBC API 函数 在 ODBC CLI 下面是网 络进程通讯(IPC) 机制 它允许不<br>同的网 络进 程互 相通 讯 不同的 IPC 机制 示例 是 Nemed Pipes 和 TCP/IP Sockets 所使用的实际 IPC 机制 依赖 于网 络协 议 这些 协议 可 下 一层即 网络协 议层 找到<br>网络协议负责发送和接收通过网络的数据流 不同网络协议的示例是 NetBEUI 和<br>TCP/IP<br>图5-1 ODBC 使用的网络组件<br>在这个通讯堆 栈的底部是物理网络 物理 网络包括适配器卡和实际连 接网络 系统所需<br>的缆线 Ethernet 和 Token Ring 是两 个最 常用 的网 络 拓扑结 构样 例<br>5.2 ODBC 组成<br>在详细 介绍 如何 编写 ODBC 应用程序 之前 先看 一看不 同的 ODBC 组件 这有助 于 理<br>解应用 程序 如何 关联 ODBC 体系结构 并且 看到 用来连 接 ODBC 兼容 数 据 库的路 径 图 5-2<br>说明了 由 ODBC 使 用的 分 层体系 结构<br>图5-2 ODBC 组件<br>5.2.1 应用 程序<br>ODBC 应用 程序要 么是 一种 像 Wo r d Excel 或者 Visual Basic 的 应用程 序 要么 是一 种<br>定制的 ODBC 应用 程序 使用 Visual Basic Visual C++ 或者一些 其 他 PC 开 发平台编写<br>ODBC 应用 程序与 ODBC 驱动 管理 程序(ODBC32.DLL) 进行静态或 动态连接 并且 ODBC<br>应用程 序调 用由 ODBC 驱 动管理 程序 提供 的 ODBC API 函数5.2.2 驱动 管理 程序<br>支持 ODBC 的应 用程 序 像 Visual Basic 调用 ODBC 驱 动管理 程序( 包括 16 位的应 用<br>程序 ODBC DLL 或 32 位的 应用 程序 ODBC 32 DLL) 注意该应用 程序 不能 直接 调用ODBC 驱<br>动程序 它可 调用 包含 在 ODBC 驱动 管理 程序 中的 函数 而 ODBC 驱 动管理 程 序程序 调<br>用相应 的 ODBC 驱动 程序 这样 就保 证 ODBC 函数 无论是 连接 到 SQL Server 还是另 外一<br>个数据 库平 台 例如 Oracle 总是 按同 一种 方式 调用 这种分 层的 体系 结构 还允 许多 个 ODBC<br>驱动程 序共 存并 且同 时激活<br>ODBC 驱动 管理 程序 负责加 载 相应 的 ODBC 驱动 程 序到内 存中 并将以后 的 请求传送<br>给正确 的 ODBC 驱动 程序 当 ODBC 驱动管 理程 序 加载一 个 ODBC 驱 动程序时 ODBC 驱<br>动管理 程序 建立 一个 指向 ODBC 驱动程序 中函 数的 指 针表 ODBC 驱动 管 理程序 使 用名为<br>连接句 柄的 标识 符来 确认每 一 个加 载的 ODBC 驱动程 序的 函数 指针<br>5.2.3 驱动 程序<br>ODBC 驱动 程序负 责发 送 SQL 请求到 关系 型数 据库管 理 系统(REDMS) 中 并且 把结 果<br>返回给 ODBC 驱动 管理 程 序 然后 ODBC 驱动 管 理程序 把这 些请 求传 送给 ODBC 客户<br>机应用 程序 每一 个兼 容 ODBC 的数据库 都有 其自 己 的 ODBC 驱 动程序 例如 SQL Server<br>ODBC 驱动 程序只 与 SQL Server 通讯 并且 不能 用于访 问 Oracle 数据 库 同样 Oracle ODBC<br>驱动程 序不 能用 来访 问 SQL Server 数据库 对于 有些 系统 ODBC 驱 动程序可 能重 新格 式<br>化 SQL 请求为由 目标 RDBMS 要求的 正确 的 SQL 语法<br>ODBC 驱动 程序处 理从 ODBC 驱 动管 理程 序中 传送过来的 ODBC 函 数调用 在每 一<br>个 ODBC 驱动 程序 中的 函 数通过 由 ODBC 驱 动 管理程 序 维护 的函 数指 针来调用<br>5.2.4 数据 源<br>使用 ODBC Administrator 程序创建 数据 源 通 过使 用 用户创 建的 名称 关联 一个 目标 关<br>系型数 据库 和 ODBC 驱动程 序 数据 源隔 离 用户和 ODBC 连 接的基 本机 制 然后 用户 使<br>用有意 义的 数据 源名 称访问 该 数据 库 但不 需 要 知道有 关 ODBC 驱动 程 序 或者关 系型数据<br>库的技 术细 节<br>当 ODBC 应用 程序 第一 次 连接到 一 个目标 数据 库时 它把 数据 源名 称传 送到 ODBC 驱<br>动管理 程序 中 然后 ODBC 驱 动 管理 程序 使用 数据源 确定 加载 哪一 个 ODBC 驱动程 序<br>ODBC 驱 动程 序 一般 需要 该关 系 型 数据库的 登录 帐户 ID 和 口令来 连接 目标 数据 库<br>在 16 位的 Windows 3.1 系统 上 ODBC 数据源 信息 存 储在 ODBC.INI 文件 中 在 32<br>位 Windows 95 和 Windows NT 系统 中 ODBC 数据 源信 息存 储在 注册 表的 两个 不同 注册 键<br>中 用户数据源信息存储在 HKEY-CURRENT-USER 系统数据源信息存储在<br>HKEY_LOCAL_MACHINE 键中 用户数据源对于每个用户是唯一的 系统数据源可以用<br>于系统 中的 全部 用户 图 5-3 示 意 了一个样 本用 户 数据 源的 注册 键图5-3 用户数据源使用的注册键<br>5.3 ODBC 应用<br>前面讲 了必 要的 预备 知识 从本 节开 始 讲解 ODBC 应 用方法<br>5.3.1 配置 ODBC 数据 源<br>在开始 使用 ODBC 之前 必须安 装一 个 ODBC 驱动 程序 并 配 置一个数 据源 当在系<br>统上安 装任 何一 种支 持 Microsoft ODBC 的应用 程序 时 一般 都安 装了 Microsoft SQL Server<br>ODBC Microsoft Office 2000 Visual Studio Enterprise<br>驱动 程序 例如 当安 装下 列任 何一 种 和<br>Edition 6 等产 品时 可 以 选择安 装 SQL Server ODBC 驱 动程序<br>只安装 SQL Server ODBC 驱 动 程序本 身 还 不能 开始使 用 ODBC 还需要创 建 一个数<br>据源 为了 创建 ODBC 数据 源 需 要运行 ODBC 管理 器 它 可 以通过 Windows 2000 的 程<br>序 >管理 工具 >数据 源 ODBC 来运 行 若系统 是 Windows 95/98 或者 Windows NT<br>则可以 在控 制面 板中 找到<br>打开 ODBC 数据 源管 理器图 标 则 显示 一个如图 5-4 的窗 口 ODBC 数 据源管 理器 程<br>序显示 所有 当前 已安 装的数 据 源 不同 的标签允 许使用 不 同类 型的 数据 源 用户 DSN 标<br>签 显示可以 由当前登录用户使用的数据 源清 单 系统 DSN 标签显示可以由系统上 全<br>部用户使用的 系统数据源清单 文件 DSN 标签显示允许连接到一 个文件提供程序的 数<br>ODBC ODBC<br>据源清 单 驱动 程序 标 签 显示 当前 安装 在系 统上 的所有不同 的 驱动程 序<br>跟踪 标签 允许 跟踪 某 个给定 ODBC 驱动程 序的 所有 活动 关于 标签 显示 由 ODBC<br>数据源 管理 器使 用的 DLL 及其版 本的 清单图5-4 使用 ODBC Administrator 增加一个数据源<br>DSN ODBC<br>为了配 置一 个新 的用 户数据 源 必须 单击 用户 标签 如果 这是 第一 次运 行<br>管理器 那么 这个 列表 是 空的 如果 已经 安装 了其 他 ODBC 驱 动程序并 且建 立 了数据 源<br>那么它 们将 出现 在 用户 DSN 或者 系统 DSN 的 列表中<br>要为 SQL Server 增加一 个数 据源 从 ODBC 数据源 管理 器对 话框 中选 择 添加 按钮<br>将进入 创建 新数 据源 对话框 如图 5-5 所示 当前 安装 在系 统上 的全 部 ODBC 驱动程<br>ODBC<br>序都列 在该 对话 框中 通 过单击 驱 动 程序的 名称 然后 单击 完成 按钮 可以选择<br>希望用 于新 数据 源的 ODBC 驱动程序<br>图5-5 选择一个 ODBC 驱动程序<br>下一个 对话 框随 所选 的 ODBC 驱动程 序 的 不同而不 同 选择 SQL Server ODBC 驱动程<br>序则显 示 Microsoft SQL Server DSN 配置向 导 如图 5-6 所示 在这个 文本 框 中 输入<br>ODBC<br>数据源 名称 可以 选择 任 意名称 其 主要 作用 是以 一 个有 意 义 的名称关联 驱动程 序<br>和目标 RDBMS 当 ODBC 第一次 企图 连接 数据 时 使用 该名 称 应用 程序 必须 指定 数据源名称 允许 用户 从所 有 已经配 置的 数据 源清 单中 选 择一个 数据 源<br>图 5-6 SQL Server DSN 配置向导 ( 界面 1)<br> 描述 是 一个 文本字段 可 以用 来帮助确认数 据源 这种数据源 描述 其实只 用于<br>ODBC 管理 器<br>最后 从下 拉组 合框 中 选择希 望 将 该数据源 连接 到 的 SQL Server 系统 网络 上的 所<br>有 SQL Server 系统都列 在这 个组 合框 中 如果 在 SQL Server 系统本身上配 置 这种 数 据源<br>那么可 以输 入名 称 (local) 这将 把数 据源 连接 到在 同一 个物 理系 统上 运行 的 SQL Server<br>单击 下一 步 按钮 则 显示下 一个 对话 框 如图 5-7 所示 这个 SQL Server DSN 配<br>置向导 的第 二个 界面 允许指 定 将要 执行 的 SQL Server 安全认 证模 式 窗口顶 端的 单选 按钮<br>允许在 使用 Windows NT 登录或 者 SQL Server 登录之间 选 择 使用 Windows NT 登录 ID 登<br>录到 SQL Server 中需要 委托连 接 并需 要实 现 Windows NT 认 证模式 或者 混合 安全 模式<br>虽然使 用 SQL Server 的认证 不要 求委 托连 接 但是 要求 用户 输入 一个 登录帐 号 ID 和口令<br>5-7 SQL Server DSN ( 2)<br>图 配置向导 界面<br>在第二个窗口中的 客户端配置 按钮允许改变用来连接 SQL Server 的 Network<br>Library 一般来说 这 些内 容不需 要改 变选择 连接 SQL Server 以获得其 它选 项的 默认 值 复选 框 表示 其 余 步骤的 默认 设置<br>从 SQL Server 系统中提 取<br>最后 如果 选择 SQL Server 认证 那么 需要 输入 用来 连接 到 SQL Server 的登 录帐 号 ID<br>和口令<br>单击 下一 步 按钮 初 始化一 个 SQL Server 的连 接 显 示如图 5-8 所 示的窗 口 在<br>SQL Server DSN 配置 向导 顶部 的复 选框 允许 忽略 在 SQL Server 登录时 指定的默认数据库<br>不选这 个复 选框 表示 使用在 SQL Server 的登 录 ID 中 输入 的 默认数据 库<br>图 5-8 SQL Server DSN 配置向导 ( 界面 3)<br>附加数据库 文件名称 复选框允许指定 一个可以附接的数据库名称 该数据库将用<br>作本次 连接 的默 认数 据库<br>选择 为准 备好 的 SQL 语句创 建临 时存 储过 程 复选 框允许 当使 用 SQLPrepare 函数<br>时 创建 一个 临时 的存 储 过程 该选 项只 能 用于 SQL Server 5.5 数据 库 SQL Server 7.0 及<br>2000 为预 准备 的 SQL 语句在 SQL Server 的 Procedure Cache 中 创建了一 个共 享 的执行 规划<br>一般情 况下 为SQL Server 6.5 数 据 库选 择这 个选 项 可以提 高使 用预 准备 语句 的 ODBC 应<br>用程序的 性能 当 选择 该 复选 框之后 出现两个单选 按钮 使用这些单选 按钮 可以确 定<br>何时删除 这些临时存储 过程 选择第 一个单选按钮 可使 这 些临时 存 储过 程在 断 开连接 时<br>删除 选择第二个单选按钮 则当以后为同样的语句句柄调用 SQL Prepare 或调用<br>SQLFreeStmt 函数 或者该 ODBC 应 用 程序 断开 连接时 删除 时临 时存 储过 程<br>选择 使用 ANSI 引用的标 识 符 复选 框则 使 SQL Server ODBC 驱 动程序在 SQL 语句<br>中对于 引号 强制 ANSI 规则 选择 使用 ANSI 空值 填充 及警 告 复选框 使 SQL Server<br>ODBC 驱动 程序对 于处 理空 列 变长 字段 上添 加尾 部空格 和对 ANSI 规则 冲 突提出警告等<br>强制 ANSI 规则<br>最后 如果 主 SQL Server 无效 请使 用备 用 SQL Server 复选框 允许 备份 SQL Server<br>系统 以防 主 SQL Server 系统的 失败 如果 为主 SQL Server 系统定义 一个备用服务器 并<br>且激活 如果 主 SQL Server 无效 请… 选项 那么 当 ODBC 驱 动程序连 接到 主服 务器<br>时 它还 检索 连接 到备 用 服务器 所需 的信 息 在对 主 SQL Server 的连 接丢失 后 ODBC 驱动程序 结束 当前 的事 务 然 后试图重新 创 建 到备 用服务 器 的连 接<br>单击 下一 步 按钮 显示 SQL Server DSN 配 置向导 如图 5-9 所示 的窗 口 它<br>指定由 SQL ServerODBC 驱动程 序使 用的 语言 字符集 区域 设置 和日 志文 件 第一 个复<br>选框指 定将 使用 哪一 种语言 显 示 ODBC 消息 如 果在连 接的 SQL Server 系统 上只安装了一<br>种语言 那 么这 个复 选框和 与 其相 关的 下拉 组合 框不能使 用<br>图 5-9 SQL Server DSN 配置向导 ( 界面 4)<br>ODBC<br>下一个 复选 框控 制 驱动程 序如 何执 行字 符集 翻 译 选择 执行字 符数 据 转换<br>复选框 指定 ODBC 驱 动 程序使 用 Unicode 转换 在 ODBC 客 户机和 SQL Server 服务器 之<br>间传送 的 ANSI 字符 串 如 果 没有 选择该 选项 那么 ANSI 数据 直 接 发送给 SQL Server<br>这要求 客户 机和 SQL Server 系统使用 相同 的代 码页<br>当输 出货 币 数字 日 期和时 间时 请使 用区 域设置 复选 框 允 许忽略在 SQL Server<br>登录信 息中 指定 的默 认设置<br>下面两个复选框允许设置长时间运行查询的最大时间( 毫秒) 并 且控制 SQL Server<br>ODBC 驱 动程序 是否 记 录 驱动程序 的统计 如果激活 这些选项 它 们 将 把活动记录在 文本<br>框中指 定的 文件 默认 设 置禁止 这两 个选 项<br>单击 完成 按钮 则完 成 SQL Server 数据 源的 配置 并且 显示 如图 5-10 所示 的确认<br>SQL Server DSN <br>对话框 该 配置向 导对 话框 提供 了 在数据 源配 置进 程中 选择 的 全部选 项<br>单击 确定 按钮则创 建新的数据源 如果在配置窗 口中显 示的任何值是 不正确 的 那么<br>可以单 击 取消 按钮 显 示前一 个 SQL Server DSN 配置 向 导界面 在这里可以单击 下<br>一步 和 上一 步 按钮 在一 系列 对话 框之 间移 动图 5-10 SQL Server DSN 配置向导 ( 界面 5)<br>单击 测试 数据 源 按 钮 则 建立一个 到 SQL Server 的连 接 并且 验证 选项设 置 如果<br>该连接不成功 则显示 一个错误信息 确认错误情况 否则 如果连接成功 那么显示 如<br>图 5-11 所示 的对 话框<br>图 5-11 SQL Server DSN 配置向导( 界面 6)<br>当安装 SQL Server ODBC 驱动程 序和 使用 ODBC 数 据源管 理器 配置 一个 数据 源 之后<br>可以准 备运 行 ODBC 应 用 程序或 者创 建自 己的 定制 客 户机/服 务器数 据库 应用 程 序<br>5.3.2 ODBC API 使 用基础<br>使用 ODBC API 比使 用数据 控 件与 Visual Basic 的内 置 DAO 方 法复杂得 多 这 主要是<br>由于要 调用 一个 外部 DLL 中的函 数 然而 使用 ODBC API 可 以提高 性能 因此 很值 得<br>使用 当应 用程 序直 接使用 ODBC API 时 它可 以调用 包 含在 ODBC 驱动 管理程 序 中的<br>函数 ODBC 驱动管理程 序在 16 位应用程序中是 ODBC.DLL 在 32 位应用程 序中是<br>ODBC32.DLL 为了 使用 ODBC API 当调 用 ODBC API 函数 时 需要 按照 一 定的顺 序图 5-12 提供 了 ODBC API 的应 用概 图<br>图 5-12 ODBC API 应用概图<br>首先必 须认 识到 所有 ODBC 应 用 程 序都必 须遵 循如 图 5-12 所 示的通 用 ODBC API 这<br>非常重 要 对 于包 容面 较 小的桌 面应 用程 序 例如 Word Excel 和 其他支 持 ODBC 的应 用<br>程序 以及 自己 写的 ODBC 客户机/服务 器应 用程 序 都是 如此 虽然 这些 很不明 显 但是<br>Word MS Qurey Excel Access 和所有 其他 支持 ODBC 的应 用 程序 都进 行同 样系列的 ODBC<br>API 调用 就像 定制 的 Visual Basic 应用 程序 一样<br>在图 5-12 中 正 像看 到的那 样 在所 有 ODBC 应 用程 序 中必须做的第一件事 情 是调<br>用 SQL AllocHandle 函 数 来创建 一个 环境 句柄 ODBC 驱动 管理 程序 使用该环境句 柄 跟 踪<br>每一个 ODBC 连接 和其 状 态<br>接下来 ODBC 应用 程序 必须 调用 SQLSeeEnvAttr 函数 这个函数注册将 要使用 的<br>ODBC API 版本 如果 没 有调用 这个 函数 那么 ODBC 驱 动程序假 设应 用程 序 使用 ODBC 2.0<br>调用约 定 如果 使用 设置为 SQL_OV_ODBC 3 其值 为 3 的 SQL_ATTR_ODBC_VERSION<br>环境属 性调 用 SQLSetEnrAttr 函数 那么 ODBC 驱动 程序知 道该 应用 程序 将使 用 ODBC 3.x<br>调用约 定<br>当设置 这些 环境 选项 之后 所有 ODBC 应用程 序必 须 调用 SQLAllocHandle 函数创 建<br>一个数 据库 连 接 句柄 当 分配数 据库 连接 句柄 时 SQLAllocHandle 函数 关联这个 新数 据 库<br>连接句 柄来 确认 在数 据库连 接 中使 用的 驱动程 序接下来 使用 由以 前的 SQLAllocHandle 函数 返回 的 数据库 句柄 使用 SQLConnect 函<br>数启动 一个 对数 据库 的 ODBC 连接 该 SQLConnect 函数 必须 提供 连接 到目 标数据 库 的数<br>据所需 源名 称和 登录 信息 SQLDriverConnect 函 数或者 SQLBrowseCcnnect 函 数 都可以 替<br>代 SQLConnect 函数 SQLDriver Connect 函 数 提示 终端 用户输入必要 的 数 据源 连接 信息<br>SQLBrowseConnect 函数 向 应用程 序返 回一 个字 符串 它确 认丢 失的 连接 信息 当应 用程 序<br>没有提供所有必要的数据源登录信息时 一般使用 SQLDriverConnect 函数 而<br>SQLBrowseConnect 函数 通 常用来 创建 定制 的登 录对 话框<br>最后一 段 ODBC 初始 化代码 是 调用 SQLAllocHandle 函数 创建 一个 语句 句柄 该语<br>句句柄用于处理 SQL 请求 当这 个语 句分 配之 后 ODBC 驱动管理程序使用该语句句柄<br>管理 ODBC 连接 的状 态信息 例如 在执 行 Select 操作 之前 应 用程 序不 能提取数据 语<br>句句柄 允许 ODBC 驱动 管 理程序 了解 ODBC 语句 的 当前状 态 因此 也知 道可 以 在给定 时<br>间执行 的有 效操 作<br>当分配 该语 句句 柄之 后 ODBC 应用程 序可 以开 始使 用该 语句 句柄 处理 SQL 请求 这<br>是 ODBC 应用 程序 所做 的 实际工 作 一个 典型 的 ODBC 应 用程序使 用这 种语 句 句 柄处理许<br>多 SQL 请求 然后 检索 和 处理这 些请 求的 结果 使用 SQLExecute 或者 SQLExecDirect 函<br>数将发 送 SQL 语句到 连接的 数 据库 中 使用 SQLExecute 函 数执行准 备的 SQL 或 者 静态的<br>SQL 语句 SQLExecDirect 函数 用于 ad hoc 或 动态的 SQL 语句 在 SQLExecute 函数可 以<br>使用之前 必须使用 SQLPrepare 函数 SQLExecute 一般用于重复执行的 SQL 语句<br>SQLExecDirect 函数 通常 用于 只执 行一 次的 SQL 语句<br>SQLExecute 和 SQLExecDirect 函 数可以 处理 任何 在主 机数 据库 上有 效的 SQL 语句<br>如果该 SQL 请求是一 个 Select 操作 它通 常创 建 一个结 果 集 该 结 果集包含了 满足 SQL<br>查询的 信息 使用 SQLFetch 函数访问 这个 结果 集 有关创 建 SQL 语句 的 详 细信息 参见<br>第 1 章和 第 2 章<br>当 ODBC 应 用程 序准 备终止时 它还 必须 执行 一些 清 理步 骤 释放 各种 ODBC 句柄占<br>用的内存 然后关闭已 启动的会话 正如用户猜测的 那样 对于每一个初 始化语 句 还有<br>一个相 应的 清理 语句 例如 当 ODBC 应用程 序终 止 时 对于 每一 个打 开的 语 句句柄 必<br>须首先 执行 SQLFreeHandle 函数 然后 对于 每一 个打 开的 连接 必须 调用 SQLDisconnect<br>函数 接下 来 它必 须调用 SQLFreeHandle 函数 释放 由每 一个 数据 库连 接句 柄占 用的 内存<br>最后 ODBC 应用 程序 必须 调用 SQLFreeHandle 函数 释放 ODBC 环境 占 用 的内存<br>刚开始 这些 似乎 非常 复 杂 但是 对于 所有 的 ODBC 应 用程序 这些 ODBC 初始化 和<br>清理函 数都 是非 常类 似的 一旦 定义 了这 些函 数 就 可以把 它 们 剪贴到其 他 ODBC 应用程<br>序中<br>5.4 ODBC 调用的前期 和后期 工作<br>既然已 经看 到了 如何 使用 ODBC API 的基 本规 则 现 在 详 细看看 如何从 Visual Basic<br>中调用 ODBC 函数5.4.1 分配 环境 句柄<br>SQLAllocHandle 函数 是必 须由 ODBC 3.x 应 用 程 序调用 的第 一个 函数 正如 在下 面的<br>程序清 单看 到的 那样 该 SQLAllocHandle 函数带 三 个参数<br>iResult% = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE, henv&)<br>If iResult% <> SQL_SUCCESS Then<br>`Error Handling<br>End If<br>第一个 参数 是一 个标 志 表 示 将要分配的 句柄 类型 SQL_HANDLE_ENV 常 量 指定将<br>要分配 的 ODBC 环境 句柄 第 二个 参数用 来表 示父 句柄 因为 ODBC 环 境是最 基本的 ODBC<br>句柄 所以它没有父句柄 SQL_NULL_HANDLE 常量用来指定一个空句柄 第三个参数<br>是一个 长变 量 它包 含由 SQLAllocHandle 函数返 回 的环境 句柄<br>SQLAllocHandle 函数 返回 一个 整数 值 表示 该函 数的 成功 或者 失败 值 SQL_SUCCESS<br>表示该 函数 调用 完全 成功 其他 值表 示有 错误<br>5.4.2 释放 环境 句柄<br>必须在 ODBC 应用程序结束之前 调用 SQLFreeHandle 函数释放环境句柄<br>SQLFreeHandle 函数 释放 由以 前的 SQLAllocHandle 函 数分配的 内存 和句 柄 下 面的代 码说<br>明如何 释放 一个 ODBC 环 境句柄<br>iResult% = SQLFreeHandle(SQL_HANDLE_ENV,ByVal henv&)<br>If iResult% <> SQL_SUCCESS Then<br> `Error Handling<br>End If<br>SQLFreeHandle 函数 的第 一个 参数 表示 将要 释放 的句 柄 类型 SQL_HANDLE_ENV 常<br>量指定 将要 释放 的 ODBC 环境句 柄 第 二个 参数 是一个长变量 它包含 将要 释 放的 ODBC<br>环境句 柄的 值<br>SQLFreeHandle 函数 返回 一个 整数 表示 函数 调用 的状态 值 SQL_SUCCESS 表示该<br>调用完 全成 功<br>5.4.3 设置 环境 属性<br>当 ODBC 环境 分配 之后 必须调 用 SQLSetEnvAttr 函数 指定 将要 使用 的 ODBC 3.x API<br>或者 ODBC 2.x API 如 果 没有使 用 SQL SetEnvAttr 函 数明确 地设 置环 境为 ODBC 3.x 那<br>么环境 默认 是 ODBC 2.x API 设置 这是 为了 确保尽可 能 与已使用 ODBC 2.x API 的 ODBC<br>应用程 序兼 容 除 了控 制 应用程 序使 用的 ODBC API 级之外 还可 以使 用 SQLSetEnvAttr<br>函数建 立连 接池 连接 池 主要用 于中 间层 的应 用程 序 它允 许 ODBC 连 接一直 打开 并可<br>以在以后 再次使用 这 样可以提高应 用程序的连接时 间 下 面的代码说明 如何设 置环境 属<br>性支持 ODBC 3.x API<br>iResult% = SQLSetEnvAttr(henv&,SQL_ATTR_ODBC_VERSION,SQL_OV_ODBC3 0)<br>If iResult% <> SQL_SUCCESS Then<br>'' Error Handling<br>End If<br>SQLSetEnvAttr 函数的第一个参数是一个长变量 它包含了环境句柄 第二 个参 数包含一个 常量 它指定 将要设 置的环 境属 性类 型 SQL_ATTR_ODBC_VERSION 常量表示 将<br>要设置 的是 ODBC 版本 第三个 参数 是一 个常 量 它包 含将 要使 用的 版本 SQL_OV_ODBC3<br>常量说 明将 要使 用 ODBC API 3.x 最后 一个 参数 指定第 三 个参 数的 长度 只有当第三 个 参<br>数包含 一个 字 符 串时 才需要 第四 个参数 如 果它 包 含一个 数字 值 正如 前面 的 示例那 样<br>可以把 最后 一个 参数 设置为 0<br>5.4.4 分配 数据 库连 接句 柄<br>当建立 ODBC 环境 句柄和 设置 ODBC 环境 属性之 后 ODBC 应用程 序就可 以使 用<br>SQLAllocHandle 函数创建一个数据库连接句柄 下面的示例说明如何创建一个数据库连接句<br>柄<br>iResult% = SQLAllocHandle(SQL_HANDLE_DBC,ByVal henv&,hdbc&)<br>If iResult% <> SQL_SUCCESS Then<br> `Error Handling<br>End If<br>像 ODBC 环境 句柄 一样 使用 SQLAllocHandle 函数 创建 数据 库连 接句 柄 为了 创建<br>一个数据库连接句柄 必须将 SQLAllocHandle 函数的第一个参数设置为<br>SQL_HANDLE_DBC 常量 下一 个参 数包 含父 句柄 在本例 中 父句柄 是以 前 创建的 环 境<br>句柄 最后 第三 个参 数 返回创 建的 数据 库连 接句 柄<br>SQLAllocHandle 函 数返 回一个整 数 表示 SQLAllocHandle 函数执行成 功 或 者 失败<br>如果返 回 SQL_SUCCESS 常量 那么 函数 执行 成功<br>5.4.5 释放 数据 库连 接句 柄<br>由 ODBC 应 用程 序创 建的每 一 个数 据库 连接 句柄都必须在应用 程 序 终止之前 释 放 下<br>面的代 码说 明如 何释 放一个 数 据库 连接 句柄<br>iResult& = SQLFreeHandle (SQL_HANDLE_DBC, ByVal hdbc&)<br>SQLFreeHandle 函数 的第 一个 参数 表示 将要 释放 的句 柄 类型 SQL_HANDLE_DBC 常<br>量指定 将要 释放 一个 数据库 连 接句 柄 第二 个参 数包含要释放 的 数 据库 连接句柄的值<br>5.4.6 连接 到数 据源<br>当 ODBC 应用 程序 创建 一 个数据 库连 接句 柄之 后 应用 程序 可以 使用 该句 柄建立对数<br>据源 的物理连 接 创建对 ODBC 数据源连接的 最常 用 方法 是使用 SQLConnect 函数 或者<br>SQLDriverConnect 函数 这两个函数将应用程序连接到某一个数据源 主要差别是<br>SQLConnect 函 数负 责提 供 建立 连接 所 需要的全 部信 息 而函 数 SQLDriverConnect 把获取<br>连接信 息的 任务 转交 给 ODBC 驱动程 序<br>使用SQLConnect 下面 的代 码说 明如 何调用 SQLConnect 函 数连接 到数 据源<br>Dim sDSN As String, sUID As String, sPWD As String<br>sDSN = "TECA SQL Server"<br>sUID = "sa"<br>sPWD = "sapwd"<br>iResult& = SQLConnect(ByVal hdbc&, sDSN, Len(sDSN), sUID, Len(sUID),_<br> sPWD, Len(sPWD))If iRESULT% <> SQL_SUCCESS And iResult% <> SQL_SUCCESS_WITH_INFO Then<br> '' Error Handling<br>End If<br>SQLConnect 函数的第一个 参数带有数据库连接句柄 它是前面 使用 SQLAllocHandle<br>函数创建 的 下一个参 数集提供必要 的登录信息 第 二个参 数是一个字符 串 它 包含应 用<br>程序将 要使 用 的 数据 源名称 第三 个参数是 一个 整数 它包 含了 数据 源名 称的 字 符串长 度<br>如果第 二个 参数 包含 一个有 效 的数 据源 名称 那么 ODBC 驱 动管理程 序加 载相 应的 驱动 程<br>序 并且 把它传送给其 他连接参数 如果该数据源参 数是一 个空指针 无 效的 或 者包含 名<br>称 DEFAULT 那么 ODBC 驱动 管理 程序 使用 在 ODBC Administrator 定 义 的 默认数 据源<br>如果默认的 数 据 源不存在 那么 SQLConnect 函 数返回 SQL_ERROR 第 四 个 参数是包 用<br>户 ID 的字 符串 第五 个参 数是 一个 整数 它包 含用户 ID 字符 串 的长度 第六 个参数 是一<br>个包含 口令 的字 符串 第 七个参 数是 一个 整数 表 示 口令的 长度<br>如果成功创建了与 数据源的连接 那么 SQLConnect 函 数返回 SQL_SUCCESS 或者<br>SQL_SUCCESS_WITH_INFO<br>使用 SQLDirverConnect 正像 在前面的示例中看到的那样 SQLConnect 函 数确认 数<br>据源和提供必要的登录信息 如果 ODBC 应用程序不能提供这些值 那么可以使用<br>SQLDriverConnect 函数 使 ODBC 驱 动 程序 在运 行时 提示输入这种 信 息 下面 的 示例说 明如<br>何调用 SQLDriverConnect 函数<br>Dim sInConnect, sOutConnect As String<br>sInConnect$ = "DSN="<br>sOutConnect$ = String$ (1024,0)<br>iResult% = SQLDriverConnect (ByVal hdbc&, ByVal hwnd&, _<br> ByVal sInConnect$, Len(sInConnect$), ByVal sOutConnect$,_<br> Len(sOutConnect$), iOutConnectLen%, _SQL_DRIVER_COMPLETE)<br>If iResult%<> SQL_SUCCESS And iResult% <> SQL_SUCCESS_WITH_INFO Then<br> '' Error Handling<br>End If<br>像 SQLConnect 函数 一样 SQLDriverConnect 函 数 的第 一个参 数是 数据 库连 接句柄<br>第二个 参数 包含 调用 应用程 序 的 Windows 句柄 或者如果 该 窗口句柄 未 知 那么该参数是<br>一个空 指针 第三 个参 数 包含一 个连 接字 符串<br>连接字 符串 可以 是空 的 它使 ODBC 驱动程序 提 示输入全部连接的 信息 或 者 它可以<br>包含任 何或 者所 有的 连接信 息 如果 提供 了连 接字 符 串 那么 它采 用下 面的 形式<br>Keywordl=value;keyword2=value;keyword3=value<br>下表列 出了 可用 于 ODBC 3.x 连接 字符 串的 有效 关键 字<br>关键字 描述<br>数据源名称<br>DSN<br>数据源文件名称(.dsn)<br>FILEDSN<br>指定的 ODBC 驱动程序名称<br>DRIVER<br>登录帐号 ID<br>UID<br>登录帐号 ID 的口令<br>PWD<br>将包含保存的连接信息的数据源文件名称<br>SAVEFILE为 SQL Server 提供全部 必要 的登 录信 息的 连接 字符 串示 例如 下<br>DSN=MySQLServer;UID=MyLoginID;PWD=MyPwd<br>DSN 关键 字表 示一 个数 据源 名称 值 它是以 前使 用 ODBC Administrator 配置的 UID<br>关键字 表示 创建 数据 源连接 时 使用 的登 录 ID PWD 关 键字表 示与 登录 ID 关 联 的口令 如<br>果省略 了这 些关 键字 的值 就像 在前 面的 代码 示例 那 样 或者 这些 值是 无效 的 那么 ODBC<br>驱动程 序显 示一 个对 话框 提示 用户 输入 必要 的连 接 信息<br>SQLDriverConnect 函数的 第四 个 参数是一 个 整 数 它 包含 连 接字符串 的 长 度 第五个<br>参数是 一个 字符 串 它包含 由 ODBC 驱动程序 成 功连接所返回的已经完 成的 连 接字符 串<br>这个字 符串 最小 是 1024 字节 可以 包含 完整 的连 接 字符串 第 六个参数 是一 个 整数 包含<br>完整连 接字 符串 的长 度 由 SQLDriverConnect 函 数返回 的 第七 个参数是一个 指 针 指向 完整<br>连接字 符串 的长 度 最 后 一个参 数是 一个 整数 它指定 ODBC 驱动 程 序是 否提示输入附加<br>的连接 信息 常量 SQL_DRIVER_COMPLE 表 示如 果包含 在 连接 中的信息不足的创建连 接<br>该驱动 程序 将显 示一 个对话 框 收 集必要 的连 接信 息<br>5.4.7 与数 据源 断开 连接<br>在应用程序终 止之前 必须与每一个连接 的数据源断开连接 下面的 代码说明如何使<br>用 SQLDisconnect 函 数断开与数据源的连 接<br>iResult% = SQLDisconnect(hdbc&)<br>SQLDisconnect 函数需要输入数据库连接句柄 并且返回一个整数值 表示 该函 数调<br>用成功 或者 失败<br>5.4.8 分配 语句 句柄<br>当分配 ODBC 环境 和数 据 库连接 句柄 并 连接数 据源 之 后 分配 语句 句柄 是最 后 必要的<br>ODBC 初始 化代码 下面 的示 例说 明如 何使 用 SQLAllocHandle 函 数创建一 个 ODBC 语句<br>句柄<br>iResult% = SQLAllocHandle(_SQL_HANDLE_STMT,ByVal hdbc&,hstmt&)<br>If iResult% <> SQL_SUCCESS Then<br> `Error Handling<br> End<br>End If<br>SQLAllocHandle 函数的第一个参数是一个常量 表示将要分配的句柄类型<br>SQL_HANDLE_STMT 常量 指定 将要 分配一 个 ODBC 语 句句柄 第 二个 参数表示父句柄<br>对于一 个 ODBC 语句 句柄 父句 柄是 数据 库连 接句 柄 最后 一个 参数 是与 ODBC 语句句 柄<br>关联的 语句 句柄<br>SQLAllocHandle 函 数返 回一个整 数 表示 SQLAllocHandle 函数 的 成功或者 失败 如<br>果返回 SQL_SUCCESS 常量 那么 该函 数执 行成 功<br>5.4.9 释放 语句 句柄<br>像环境 句柄 和数 据库 连接句 柄 一样 所有 由 ODBC 应 用程序分 配的 ODBC 语 句 句柄都<br>必须在应 用程序终止之 前释放 然而 不像环境句柄 和数据 库连接句柄 这两 个 句柄都 使用 SQLFreeHandle 函数 而释 放语 句句 柄 可以使 用 SQLFreeHandle 或者 SQLFreeStmt 函数<br>使用 SQL FreeStmt SQLFreeStmt 函数可以 关闭 由语句 句 柄使 用的 游标 它可 以 解除与 某<br>个给定 的语 句句 柄相 关联的 参 数 或者 释放 由 ODBC 语 句句柄 使用 的全 部资 源 下面 的代<br>码显示 了一 个调 用 SQLFreeStmt 函数的示例<br>iResult% = SQLFreeStmt(hstmt&,SQL_CLOSE)<br>SQLFreeStmt 函数的第一个 参 数是 以前 由 SQLAllocHandle 函 数创建 的语 句句 柄 第二<br>个参数是一个 整数 它控 制执行 的释放语句句 柄操作的类型 SQL_CLOSE 常量关闭一个<br>与语句 句柄 关联 的打 开的游 标 其 他常 用的常 量是 SQL_UNBIND SQL_RESET_PARAMS<br>和 SQL_DROP SQL_UNBIND 常量释放 使 用 SQLBindParameter 函数绑定 到语句 句 柄的 全<br>部列 SQL-RESET-PARAMS 常量 释放 用 SQLBindParameter 函数 绑定 到语 句句柄 上 的全 部<br>参数 SQL_DROP 常量 使 SQLFreeStmt 函数释放全 部与 该语 句关 联的 资源<br>使用 SQLFreeHandle SQLFreeHandle 函数还可以用来释放 ODBC 语句句柄<br>SQLFreeStemt 函数比 SQLFreeHandle 函数 提供了 更 强的控 制 度 实际 上 SQLFreeHandle<br>函数等 价于 使用 SQL_DROP 常量的 SQLFreeStmt 函数 使用 SQL FreeHandle 函数是释放<br>由 ODBC 语句 句柄 使用 的 全部资 源 的首选 方法 这个函 数一 般用 作终 止应 用程序 进 程的 一<br>部分 由 SQLFreeStmt 选项提供 的附加灵 活性 使 其成为 其 他用户的更好选择 下 面的代 码<br>示例说 明如 何调 用 SQLFreeHandle 函数 释放 一个 语句 句柄<br>iResult% = SQLFreeHandle(SQL_HANDLE_STMT,ByVal hstmt&)<br>SQLFreeHandle 函数 的第 一个 参数 表示 将要 释放 的句 柄 类 型 SQL_HANDLE_STMT<br>常量指 定将 要释 放一 个语句 句 柄 第二个 参数 是将 要 释放的 语句 句柄<br>提示 在初 始化 过程中 大多数 应用 程序 还使 用 SQLGetInfo 函数 SQLGetInfo 函数<br>不是一 个必 选函 数 对 于 检索有 关 ODBC 驱动 程序 功 能的信 息 它是 很有 用的<br>另外 还常 常使 用 SQLTables 和 SQLCloumns 函数检 索数据 库中 有关 表和 列的 信<br>息<br>5.5 通过 ODBC 访问 SQL Server 数据 库<br>上一节 研究 了全 部 ODBC 应 用 程序所 需的 ODBC 初 始化和 终止 函数 在这 一节 将介<br>介绍一 些基 本的 ODBC 数 据访问 函数 用来 从 ODBC 数 据源上 检索 插入 或者修 改 信息<br>SQL 是 ODBC 数据 访问 的核 心 使用 以前 由 SQLAllocHandle 函 数创建 的语 句句 柄 把 SQL<br>语句从 ODBC 应用 程序 中 发送到 数据 源 ODBC 应用程序 可以 发送 任何 有效 的 SQL 命令<br>到主机数据库 ODBC 应用程序可 使用动态的 SQL 语 句或者预准备的 SQL 语句 使用<br>SQLExecDirect 函数 执行 动态 的 SQL 使用 SQLPrepare 和 SQLExecute 函 数的组 合执行 预<br>准备的 SQL<br>5.5.1 使用 动态 的 SQL 和SQLExecDirect 检索数 据<br>使用 SQLExecDirect 函 数 执行动 态的 SQL SQLExecDirect 函数 判断 SQL 语句 并且<br>在执行 SQLExecDirect 函 数时创 建数 据访 问路 径<br>SQL Select 语句 可能 是最基 本 的 SQL 操作 所 以使用 SQL Select 语句 从 ODBC 数据源 中检索信 息是 本章 讨论 的第一 种 ODBC 数据访问 ODBC 的 强 大功能之一 就是 执 行 ad hoc 查<br>询 连接 多个 文件 和检 索 动态的 数据 集 SQL Select 语句是 这些 ad hoc 查 询的执 行者 下<br>面的代 码示 例说 明如 何使用 SQLExecDirect 函数 调用一 个 SQL Select 语句 从表中 检 索 指<br>定的行 集<br>sSQL$ = Select from authors where state = `OR''<br>iResult% = SQLExecDirect(hstmt&,sSql$,Len(sSql$))<br>If iResult% <> SQL_SUCCESS Then<br> `Error Handling<br>End If<br>首先 把 SQL Select 语句赋给一个字符串 变量 在本例 中 赋给变量 SQL$ 的字符 串<br>选择表 authors 中的 全部 行 其中 state 列的 值等 于 OR 常量 接下来 SQLExecDirect 函数<br>在主机数据库中执行该 SQL 语句 SQLExecDirect 函数的第一个参数是一个以前使用<br>SQLAllocHandle 函数 创建 的 ODBC 语句句 柄 在该 语句 句柄 之后 下一 个参 数包 含了 该 SQL<br>语句 第三 个参 数是 一个整 数 它包 含将 要执 行的 SQL 语 句的长 度<br>注意 使用 SQLExecDirect 函数 运行 的 SQL 语句不 包 括变量 所有 数据 选择条件都使<br>用文字 在 SQL 字符 串中 编 码 例如 在前 面的 示例 中 只选择那些 state 列包<br>含了文 字 OR 的行 构造 如果 希望 选择 一个 行集 其中 state 列 包含了一 个不 同<br>的值 那么 需要 重新 构造一 个 新字 符串 它包 含使 用 不同文 字值 的 SQL Select<br>语句 然后 可以 执行 这 个新的 SQLExecDirect 语句<br>SQLExecDirect 函数 返回 一个 整数 它报 告该 操作 的 成功或 者失 败 如果 SQLExecDirect<br>函数在 主机 数据 库上 执行成 功 那么 创建 了包 含满 足 SQL Select 语 句 操 作条件的 数据 的 结<br>果集 SQLExecDirect 函数 返回 SQL_SUCCESS 在本章后面 可以看到如何处 理这个 结<br>果集 如果 该函 数由 于 SQL 无效而 失败 因为 该用 户 没有授 权请 求的 对象 或者 由于 其他<br>原因 那么 SQLExecDirect 函数 通常 返回 SQL_ERROR(-1) 应用 程序 可以 使用返 回 的代 码<br>和 SQLGetDiagRec 函数 确 定各种 错误 的原 因 ODBC 错 误处理 和 SQL DiagRec 函数也在本<br>章后面 讨论<br>5.5.2 使用 SQLExecute 和 预准备 SQL 语句<br>动态地 创建 和执 行 SQL 语句的 功能 给了 ODBC 应用 程序 极 大 的灵活性 然而 为这<br>种灵活 性付 出的 代价 是性能下 降 每次 SQLExecDirect 函 数执行 时 判断 SQL 语 句 和创建<br>必要的数 据访问路径意 味着在结果返 回给终端用户之 前 必 须 等 待所有这 些操 作 完成 对<br>于那些 每一 个数 据库 查询的 SQL 语句 有很 大变 化的 ad hoc 查 询情况 实际 上 ODBC 应用<br>程序不 能显 著提 高性 能 然而 对于 那些 执行 预定 义 的 SQL 语 句集 的 应 用程序或者 对于 那<br>些执行 重复 SQL 语句的应用 程 序 SQLPrepare 函数 和 SQLExcute 函 数在灵活 性 和性能 方<br>面都有 都有 很大 提高 SQLPrepare 函 数 在主 机数 据库中执行 SQL 语 句要求 许多 开支 当调<br>用 SQLPrepare 函数 时 判断 SQL 语句 和打 开游标 这就给 SQLExecute 函 数留 下了运 行已<br>经准备 的 SQL 语句的 任务<br>使用 SQLPrepape 和 SQLExecute 函数 相对 于执行 一 次的 SQL 语句 并没有明显的<br>优势 执行 一个 SQLExecDirect 函数 所需 的时 间 与 执行一 次 SQLPrepare 和 SQLExecute<br>函数所需 的时间几乎是 相等的 因为 这两种方法都执 行同样 的步骤 然而 相对 于执 行多次的 SQL 语句 组合 SQLPrepare 和 SQLExecute 函数将大 幅 度 提高性 能 SQLPrepare 语<br>句只需 要调 用 一 次 它负 责分 析 SQL 语句语 法并打 开游标 之后 比较 快的 SQLExecute<br>语句可以运行需要的次数 移动游标打开的开支到 SQLPrepare 语句使得重复执行<br>SQLExecute 函数 比重 复执 行 SQLExecDirect 函数 快得 多<br>sSql$ = "Select from authors where state = “ ? “<br>`Prepare the SQL Statement<br>iResult% = SQLPrepare(hstmt&,sSql$,Len(sSql$))<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>`Bind the parameter<br>iResult% = SQLBindParameter(ByVal hstmt&,1,SQL_PAREM_INPUT,_<br> SQL_C_CHAR,SQL_CHAR,4,0,sSstate,4,Istate)<br>If iReslt% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>''Run the prepared SQL statement using the statement handle<br>iResult% = SQLExecute(hstmt&)<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>使用 SQLPrepare 和 SQLExecute 函数的 组合 非常 类似 于以 前看 到的 SQLExecDirect 函<br>数 首先 给字 符串 变量赋予了 一 个 SQL 语句 其次 执行 SQLPrepare 函数 SQLPrepare<br>语句带 的参 数与 SQLExecDirect 函数 要求 的参 数完 全 一样 第一 个参 数是 ODBC 语句句 柄<br>第二个 参数 是包 含将 要执行 的 SQL 语句 的字 符串 第 三个参 数是 SQL 字 符串的 长度 然<br>而 SQLPrepare 语句 实际 上不 在数 据源 上运 行 SQL 语句 记 住这一点很 重要 它只是 为<br>SQL 语句的 运行 做准 备工作<br>注意 使用 SQLPrepare 和 SQLExecute 执行的 SQL 语 句可以 包括 变量 也称 为参数<br>其参数 是利 用 SQLBindParameler 函数 实现 与 SQL 语 句相关联<br>接下来 在前 面的 示例 中 可以看 到 SQL 使用了 一个问 号(?)代 替文字 OR 当 SQLPrepare<br>函数成 功完 成之 后 调用 SQLExecute 函 数实际上执行 SQL 语句 该 SQLExecute 函数只<br>带一个 参数 这就 是 ODBC 语句句柄 这个 ODBC 语 句句柄必 须与 以前 SQLPrepare 函数<br>使用的 ODBC 语句 句柄 相 同 当该 SQLExcucte 函数 成功完 成之 后 包含 满足 SQL 请求的<br>数据的 结果 集可 以用 于 ODBC 应用程 序<br>5.5.3 使用 结果 集<br>既然 已 经 看 到 了如何将 SQLSelect 请 求发送到数 据 源 下 一步就是检索 由 返 回 到 应用<br>程序的 SQL 请求创建 的结果 集 SQLFetch 函数是基本 的 ODBC 函数 允许 浏览 结果 集<br>检索 结果 集中 的数 据 有 两种不 同的 方 法 或者使 用 SQLGetData 函数逐个处理结果集的<br>每一个 元素 或者 使用 SQLBindCol 函数绑 定所 选列 到程 序变 量中<br>使用未绑定的列和 SQLGetData 当使用 SQLExecDirect 或者 SQLPrepare 和<br>SQLExecute 函数 对执 行 SQL 语句 生 成了结 果集 之 后 包含 数 据 的结 果集返回到 应 用 程序中 使用 未绑 定的 列和 SQLGetData 函数 是处 理 Visual Basic 中 结果集 的最 直 接了当 的方<br>法 因为 不需 要涉 及任 何 Visual Basic 的内存 管理 问 题 然而 正 如在 下面 两个示 例中 将要<br>看到的 那样 使用 SQLGetData 函数 需要 更多的 代码 并且 没有 使用 绑定的参 数 那样清 楚<br>下面的 示例 说明 如何 使用 SQLFetch 和 SQLGetData 的 组合从 ODBC 结 果集中 检 索数据<br>确定结 果集 中的 列数<br>iResult% = SQLNumResultCols(hstmt&,iNbrColumns%)<br>If iResult% <> SQL_SUCCESS Then<br> ''Error handling<br>End If<br>获取结 果集 中的 所有 行<br>Do Whlt SQLFetch(hstmt&) <> SQL_NO_DATA_FOUND<br>SRecord$ = " "<br> ''Loop through all of the columns in the result set<br> For i% = 1 To iNbrColumns%<br> iResult% = SQLGetDate(ByVal hstmt&,i%,SQL_CHAR,sBuffer,_<br> Len(sBuffer),1BufferLen&)<br> ''Add each column to the sRecord string separated by Tabs<br> sRecord$ = record$ & Chr$(9) & Left$(buffer,bufferLen&)<br>Next i%<br>ODBC SQL NumResultCols<br>为了检 索未 绑定 结果 集中的 数 据 需要 调用 的第 一个 函数 是<br>函数 正如 其名 称一 样 它返回 结果 集中 的列 数 SQLNumResultCols 函数 带两个 参 数 第<br>一个参 数是 由前 面的 SQLExecDirect 或者 SQLExecute 使 用的语 句句 柄 第二 个 参数是 一个<br>整数 它包 含结 果集 中的列 数 就像 所有 其他 的 ODBC 函 数一样 SQLNumResultCols 函<br>数返回 一个 整数 表示 该 函数调 用的 成功 或者 失败<br>SQLFetch SQLFetch<br>接下来 使用 函数检索 结果 集中 的每一 行 每一 次成 功地 调用 函<br>数都将 检索 结果 集中 的一行 SQLFetch 函数使 用在 以前 的 SQLExecDirect 或者 SQLExecute<br>函数中 使用 的语 句句 柄 对于每 一次 成功 的调 用 SQLFetch 函数都返回 值 SQL_SUCCESS<br>当 SQLFetch 函数从结果集中检索最后一行时 SQLFetch 函数返回值<br>SQL_NO_DATA_FOUND(100)<br>SQLFetch For-Next<br>在前面 的示 例中 可以 看 到在每 一个 函数之后 在 循环中 间 调<br>用 SQLGetData 函数 对于 结果 集中 的每 一列 执行 SQLGetData 函数 该 函 数提 取一个给<br>定列的 值 并 且把 它移 动 到一个 程序 值中 SQLGetData 函 数带六 个参 数 像其 他的 ODBC<br>检索函 数一 样 SQLGetData 函数 的第 一个 参数 是语 句句 柄 它必 须与 SQLFetch 函数使用<br>的语句 句柄 一样 第二 个 参数是 一个 整 数 它包含 SQLGetData 函 数从列值 中提 取的 数据<br>提示 当使 用 SQLGetData 函数 时 结果 集中 的列 号 从 1 开 始 另外 结果 集中的 列必<br>须按照 升序 方式 访问<br>第三个参数表示列的 SQL 数据类型 第四个参数是一个缓存区 包含了成功完成<br>SQLGetData 函数 之后 的列值 第五 个参 数是 一个 输入 参数 它包 含第 四个 参数 的长 度 第<br>六个参 数是 一个 输出 参数 它包 含由 SQLGetData 函 数检索的 数据 长度<br> Visual Basic<br>使用绑 定列 和 SQLBindCol 在 中使用 未 绑定 的 列 比较直观 但使用绑 定列要求比 较少的代码 并且从结果集 中返回的列中检 索数据 时 这是一种 比较好 的方法<br>然而 Viual Basic 处理 指针 的 有 限能力使 得这 种解 决方 案比 使用 带绑 定列 的 SQLGetData 复<br>杂 就像 在下 面的 示例 中 看到的 那样 SQLBindCol 函 数带一 个指 向变 量 的指 针 作为其 一<br>个参数 并且 它希 望变 量 在内存 中的 位置 从执 行 SQLBindCol 函 数到以 后执 行 SQLFetch 函<br>数都是 一样 的 对于 C 应用 程序 这不 是问 题 然而 Viual Basic 可在内 存 中自由移动字<br>符串 变量 这 就意 味着它 们的位 置 可 能会 改 变 从而使 SQLBindCol 函 数失败 解决 这种<br>问题 的一 种 方 法是 在 SQLBindCol 函数 中使 用 一 个位 数 组 代替 一个字符 串变 量 下面 的代<br>码说明 如何 使用 位数 组和 SQLBindCol 函数 从结 果中 检索 数据<br>Dim bBuffer(256) As Byte<br>Dim 1BufferLen As Long<br>iResult% = SQLBindCol(hstmt&,1,SQL_C_CHAR,bBuffer(0),_<br> 256,1BufferLen)<br>Do While SQLFetch(ByVal hstmt&) < >SQL_NO_DATA_FOUND<br> List1.AddItem ByteArray2String(bBuffer())<br>Loop<br>正如看 到的 那样 SQLBindCol 函数的 参数 非常 类似 在 SQLGetData 函 数上使用 的参 数<br>SQLBindCol 函数的第一个参数是语句句柄 它必须与在以前的 SQLExecDirect 或者<br>SQLExecute 语句 上使 用的 语句 句柄 相 同 第二 个参 数是 结果 集中 的列数 第三 个参数 是列<br>数据类型 第四个参数 是指向缓存区 的指针 它将 包 含列数 据 这个指针 作为位 数组中 的<br>第一个 元素 传送<br>提示 当位 数组 作为参数到 一 个外 部函 数 时 总 是使用(0) 索引传 送函 数到 该数组 的 第<br>一个元 素<br>第五个参数是 一个输入参数 它包含缓存 区的长度 第六个参数是一 个输出参数 它<br>将包含 当 SQLFetch 完成时返 回数 据的 实际 长度<br>在这个示例中 可以看到 没有 必要 把数 据明 确地 移 动到每 个 SQLFetch 函数之后<br>SQLBindCol 函 数自 动执 行 指定数 据的 转换 并且用指 定 列 的值填充 绑 定的变量 在本 例 中<br>是一个 位数 组<br>然而 当使 用位 数组 时 还有一 个另 外的 考虑 Visual Basic 以 Unicode 格 式存储位 数<br>组 可能 在 ODBC 应用 程 序可以 使用 这个 字符 数据 之 前 需要 把 Unicode 位 数 组转换为 Visual<br>Basic 字符 串 在前 面示 例 中调用 的 ByteArray2String 函数如下<br>Private Function ByteArray2String(bArray() As String<br> Dim sString As String<br> Dim nStringLen As Integer<br> ''Convert the string to Unicode<br> sString = StrConv(bArray(),vbUnicode)<br> nStringLen = InStr(sString,Chr(0)) - 1<br> ''Return the Unicode string<br> ByteArray2String = Left(sString,nStringLen)<br>End Function这个函 数把 这个 位数 组作为 输 出 并且 使用 Visual Basic 的 StrConv 函数把 Unicode 位<br>数组转 换为 ASCII 然后 在 得到 的 字符串 中搜 索 Null 字符( 这是 C 语言中字符串的终止<br>字符) 然后 在 Null 字符 处切 断返 回的 字符 串<br>5.6 通过 ODBC 修改 SQL Server 数据 库<br>正像可 以使 用动 态的 或者静 态 的 SQL 执行 SQL Select 语句 一样 还 可以使 用同样 的<br>SQLExecDirect 或者 SQLPrepare 和 SQLExecute 函 数执行 其它 SQL 语句 可以使 用应用 到<br>Select 语句 的同 样标 准 在 SQLExecDirect 以及 SQLPreqare 和 SQLExecute 函 数 之间选择<br>如果该 SQL 语句只执 行一次 那么 一般 使用 SQLExecDirect 函数 如果 该 SQL 语句要 调<br>用许多次 或者希望把不同的参数传送到 SQL 语句中 那么一般使用 SQLPrepare 和<br>SQLExecute 语句 在下 一节 将会 看到 如何 用 预准备 的语 句和 一个 打开 的游 标 调用一些<br>基本的 SQL 语句来插 入 删除 修改 数据 行<br>5.6.1 插入 数据 行<br>在理论 上 插入 数据 行的 ODBC API 代 码非 常类 似于用来检索 数 据 的代 码 首先 ODBC<br>应用程序必须执行全部必要的 ODBC 初始化步骤 包括分配语句句柄 接下来, 使用<br>SQLPrepare 函数 准备 该语 句 使用 SQLBindParameter 语句把该 语句 使用 的参 数绑 定到 语句<br>上 最后 使用 SQLExecute 函数 执行 该 SQL 语句 主要差 别是 SQL 语句 本身的差别 使<br>用 Insert 语句替代 使用 Select 语句 下面 的代 码示 例说 明如 何使 用 ODBC 插入 数据<br>`Prepare the Insert statement<br>sSql$ = “Insert into department values (?,?)”<br>iResult% = SQLPrepare(hstmt&, sSql$, Len(Sql$))<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>''Bind the parameters<br>iResult% = SQLBindParameter(ByVal hstmt&, 1, SQL_PARAM_INPUT,_<br> SQL_C_LONG, SQL_DECIMAL, 4, 0, Dep_ID, 4, lpLenDep_ID)<br>If iRESULT% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>iResult% = SQLBindParameter(ByVal hstmt&, 2, SQL_PARAM_INPUT, _<br> SQL_C_CHAR, SQL_CHAR, 25, 0, bDep_Name, 25, lpLenDep_Name)<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>''Now Execute the Insert statement<br>iResult% = SQLExecute(ByVal hstmt&)<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If使用 ODBC API 插 入数 据 的第一 步是 分配 一个 SQLInsert 语句到一 个字 符串 中 不必<br>奇怪 插入 数据 行的 SQL 语句是 Insert 语句 有关编写 SQLInsert 语句的 详细 信息 参见 第 1<br>章和第 2 章 注意 这个 Insert 语句 两个 参数 标记 用作 变量的 占 位符 当该语句 执行 时<br>这些变 量将 传送 到该 语句中<br>接下来 使用 SQLBindParameter 绑定 两个参数 到程序变 量中 该 SQLBindParameter<br>函数非 常类 似前 面看 到的 SQLBindCol 函数 第一 个参 数是 在 SQLPrepare 函 数 中使用的语<br>句句柄 第二 个参 数是 SQL 语句的 参数 序号 其中 1 表 示第一个 参数 2 表示第 二 个 参数<br>等等 第 三个参数 是 一 个 整 数 表示 该参数是输入 输出或者输入/ 输出参数 第四 个参数<br>指定在 ODBC 应用 程序 中 使用的 参数 的 PC 数据类 型 第 五个 参数 指定 在数据库中使 用 的<br>数据 类 型 当调 用 SQLExecute 函数 时 ODBC 驱动 程序 负 责 在这 两个 数据 类型 之间 转 换<br>参数数据 第六个 参数是一个整数 指定数据列的大 小 第七个参数表示小数的 位数 正<br>如希望看 到的那样 对 于 字符数 据或 整数 该参数值总是 0 第 八个参数 是一 个 指向 缓 冲<br>区的指针 它包含了在参 数中使 用的数据 第九个参数是一个输 入参数 它包 含 了在 第 八<br>个参数中 使用的数据 第九个参数是 一个输入参数 它包 括 在 第八 个参数 中使 用 的参数 据<br>库的长 度 最后 第十 个 参数是 一个 指向 数 据缓冲 区 长 度的指针<br>提示 绑定到 ODBC 语句句 柄的 参数 标记依然起作 用 直到 使用有 SQL_DROP 选项的<br>SQLFreeStnt 关闭 该语 句 或者使用 SQL_RESET_PARAMS 选 项重新 设置 语句 参数<br>为止<br>对于在 SQL 语句使用 的每一 个 参数 标记 都 需要运 行 SQLBindParameter 语句 在前<br>面的示 例中 SQLInsert 语句使 用两个 参数 标记 因 此应用 程序 需要 运行 SQLBindParameter<br>函数 两次—— 每一 个 参 数 一次 在所 有必要的参数 标记绑定之后 可以使用 SQLExecute<br>函执行 该 SQL 语句 并 且 把 数据插入 到数 据库 中<br>5.6.2 删除 数据 行<br>使用预 准备 SQL 从 ODBC 数据库 中删 除行 与 在使用 Insert 语 句中看 到的 操作 完全 一<br>样 首先 SQL 语句加上任 何 参数 标记 传送 到一 个字符 串 中 其次 使用 SQLBindParameter<br>语句把 参数 绑定 到程 序变量 上 最后 使用 SQL Execute 命 令执行该 SQL 语句 下面的 代<br>码示例 说明 如何 使用 ODBC 删除行<br>''Prepae the Delete statment<br>sSql$ ="Delete from department where Dep_ID = ?"<br>iResult% = SQLPrepare(hstmt&,sSql$,Len(sSql$))<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>''Bind the parameters<br>iResult% = SQLBindParameter(ByVal hstmt&,1,SQL_PARAM_INPUT,_<br> SQL_C_LONG,SQL_CHAR,4,0,Dep_ID,4,lpLenDep_ID)<br>If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>iResult% = SQLExecute(ByVal hstmt&)If iResult% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>正如看 到的 那样 用来 执 行删除 操作 的 ODBC 函数与 用来 执行 插入 操作 的 ODBC 函数<br>完全相 同 主要 差别 是 SQL 语句本 身 在这 个示 例中 Delete 语句 只 带有一 个参 数——在<br>这个示 例中 正好 是 department 表的主 键 因为 Delete 语句 只 使 用了一个参数标 记 所以<br>该代码 只需 要使 用一 个 SOLBindParameter 函数 当调用 SQLExecute 函数 时 才 执行实 际<br>的删除 操作<br>提示 对于 使用 不同 参数的 SQL 语句 使用 多个 语句句 柄 如果 ODBC API 应 用 程序需<br>要执行 各种 不同 的 SQL 语句 其中每 一个 语句 要求 一 个不同 的 参数集 那么 处<br>理这 种情 形的 最简 单方 法 是使用多条语句 在这种 结构中 每一 条语 句都 有 自<br>己的参 数绑 定集<br>5.6.3 修改 数据 行<br>使用 ODBC 预准 备的 语句和 游 标修 改行 与 在 插 入和删 除操 作中所使 用 的 ODBC 函数<br>集完全 一样 重复 一遍 主要差 别是 SQL 语 句 本身 和 必须绑 定的 参数 下面 的示 例说 明如<br>何使用 ODBC API 修 改一组 列<br>''Prepare the Update statement<br>sSql$ = "Update DEPARTMENT Set Dep_Name = ? where Dep_ID = ?"<br>result% = SQLPrepare(hstmt&,sql$,Len(sql$))<br>If result% <> SQL_SUCCESS Then<br> ''Error handling<br> End If<br>''Assign the parameter values<br>nDep_ID = CLng(Text_DeptNumber.Text)<br>''Convert the string to a byte array for parameter binding<br>String2ByteArray bDep_Name,Text_DepDesc.Text<br>''Setup the binding lengths<br>lpLenDep_ID = 4<br>lpLenDep_Name = 25<br>''Bind the parameters<br>result% = SQLBindParameter(ByVal hstmt&,1,SQL_PARM_INPUT,_<br> SQL_C_CHAR,SQL_CHAR,25,0,bDep_Name(0),25,lpLenDep_Name)<br>If result% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>result% = SQLBindParameter(ByVal hstmt&,2,SQL_PARAM_INPUT,_<br> SQL_C_LONG,SQL_DECIMAL,4,0,nDep_ID,4,lpLenDep_ID)<br>If result% <> SQL_SUCCESS Then<br> ''Error HandlingEnd If<br>result% = SQLExecute(ByVal hstmt&)<br>If result% <> SQL_SUCCESS Then<br> ''Error handling<br>End If<br>该 SQL Update 语句 使用 第 一个参 数标 记 的 值修改列 Dep_Name 其中 列 Dep_ID 的值<br>等于在 第二 个参 数标 记中使 用 的值 在这 个示 例中 因为列 Dep_ID 是 一个唯一 键 所以 SQL<br>语句只 修改 一条 记录 然而 Update 语句 可以 修改 任何数 量 的行 只要 其满 足 在 Where 子句<br>中指定 的选 择条 件<br>在本章前面的示例 中 可以看到如何使用 ByteArray2String 函数检 索在参数中返回的<br>位数组 的内 容 并 且把 该 值 放在一个 字符 串变 量中 这里示 意的 参数 化 的 Update 语句有 完<br>全相 反的要求 在执行 Update 操作之前 字符串 变量 的内容 必须 移动 到一 个位数组 中<br>String2ByteArray 函数执行这 种 任务 下面 的代 码清单 列 出了 String2ByteArray 函数的 源代<br>码<br>Private Function String2ByteArray(bArray() As Bytem,sString As String)<br> Dim nStringLen As Integer<br> Dim i As Integer<br> ''Move each element of sString to bArray<br> For i = 0 To Len(sString) - 1<br> bArry(i) = Asc(Mid(sString, i+1, 1))<br> Next i<br> ''Zero out all remaining array items<br> For i = Len(sString) To UBound(bArray)<br> bArray(i) = 0<br> Next i<br>End Function<br>像前面 讨论 过的 插入 和删除 操 作一 样 该修 改函 数首先 使 用 SQLPrepare 函 数准备该 语<br>句 其次 调用 两次 SQLBindParameter 函数 每一 个 参数标 记一 次 最后 当调 用 SQLExecute<br>函数时 执行 Update 语句 像前 面的 那些 示例 一样 重要的 是在 SQLPrepare 中使用 的 语<br>句句柄 匹配 在以 后的 SQLBindParameter 和 SQLExecute 操作 中 使 用的语 句句 柄<br>5.6.4 数据 并发 性<br>当修改 多用 户数 据库 时 例如 SQL Server 数 据 并 发性是 碰到 的一 个主 要问 题 当 SQL<br>Fetch 操作检 索一 行时 一 般没有 把 锁放在 所选 择的 行上 如果 多个用 户同时修改一 个 数 据<br>库 那么 可能有多个用 户同时修改同 一行 如果发生 这种情 况 并且没有 强制数 据并 发性<br>的机制 那 么第 一个 用户的 修 改可 能被 第二 个用 户的修改所覆 盖 使用 SQL Server 实现数<br>据并发性主要有 两 种 方 法 可以使用一 种 数 据 编 码技 术 叫做 优化 记录 锁定 或利 用 SQLServer 的行级 锁定 功能 的行 级锁 特征<br>优化记录 锁定 确保数据 完整性 的一种常 用方法是 使用优化记录锁定 优化并发性是<br>在允许 修改 操作 之前 检 查 要 修改的列值 来实 现 的 例如 考虑 下面 简单 的 SQL Update 操<br>作<br>Update departent Set Dep_Name = ? Where Dep_ID = ?<br>该 Update 语句 没有保 护数据 并 发性 Dep_Name 列值 总是 由最 后一 个 执行 Update 语<br>句的用 户设 置 使用 优化并 发 性 这个 SQL Update 语 句可以 改成 下面 的形 式<br>Update department Set Dep_Name = ? Where Dep_ID = ? And Dep_Name = ?<br>在这个 示例 中 And 子句提 供了 一种 机制 检查 Dep_Name 列 自从数 据检 索以 来是 否<br>修改过 在 And 子句 中使用 的值 是最 初检 索到 的 Dep_Name 列值 如果 另一 个用 户修 改这<br>个特殊 行的 Dep_Name 列 那么 这个 Update 语 句将失 败 因为 Dep_Name 字 段 不再等 于以<br>前检索 的值 使用 And 子句 实现 优化 数据 并发 性似 乎是 该问 题的 一个 很好 的解决 方 案 但<br>是并非 全部 的 Update 操作 都像 这个 示例 一样 简单 如果 Update 语句需要 处 理 40 或者 50<br>个列 那么 就无 法想 象这种 修 改操 作会 发生 什么 样的情 况 这种 解决 方案 显然 是 不明智 的<br>这个问题 的另外一种解决 方案是 在表中增 加一个时戳列 使用时 戳列实现 优化并 发性需 要<br>如下一 条 SQL Update 语句<br>Update department Set Dep_Name = ? Where Dep_ID ? And Time_Stamp = ?<br>使用这 种方 法 不管 修改了 多 少列——SQL Update 语 句只需 要增 加一 个 And 子句 然<br>而 使用 时戳 实现 优化 记 录锁定 要求 具有 修改 表结 构 的能力 并非 所有 的 ODBC 应用程 序<br>都有这 种能 力<br>5.7 通过 ODBC 调用 SQL Server 数 据库的存储 过 程<br>调用 存储 过程非常 类似 于 执行预 准备 的 SQL 语句 然而 与 当程序执行时 才创 建 的<br>ODBC 预 准备语 句不 同 存储过程 必须在其 调用之前 存 在 使 用 一 个简单的 文本编辑 器<br>就可以 创建 SQL Server 存储过程 然而 创建 存储 过程 最常 用的 方法 是使用 SQL Server 查<br>询分析 器<br>图 5-13 创建一个插入记录的存储过程图 5-13 示意 了如 何使用查询 分 析器创 建一个 插入 记录的 简单的 存储 过程 该 spInsert<br>存储过 程向 表 department 中插入记 录 这个 存储 过程带 两 个参 数 第一 个参 数是一 个 整数<br>与表中 第一 个列 相对 应 第二个 参数 是一 个 25 字 节的字符字段 它与表中第 二 个列 相对 应<br>与 Insert Update 和 Delete 不同 预准 备语 句使 用以前 讲 过的 ODBC 游标 SQL Server<br>存储过程 在第一次执行 时预编译 并 且在数据库中保 存为存储过程对象 另外 不像使 用<br>预准备 语句 创建 的 ODBC 语句那 样 当客 户程 序终 止 以后 存储 过程 继续 存在数 据 库中<br>提示 可以 使用SQL Server 提供的 sp_stored_procdures 存 储过程列出 某个 给 定数 据<br>库的存 储过 程清 单 另外 可以 使用 sp_helptext 存储 过 程来 查看 某个已有的<br>存储过 程的Transact SQL 源代 码<br>执行存 储过 程所 需的 ODBC 函数调用 序列 非常 类似 ODBC 游标所需的 ODBC 函数调<br>用 但是 也有 些不 同 下 面的示 例说 明准 备 ODBC 语句名柄所需的 Visual Basic 代码 它<br>调用了 存储 过程 spInsert<br>result%=SQLALLocStmt(ByVal dbHandle&, hstmt2&)<br>If result@<>SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>''Prepare the SP statememt handle<br>sql$="(Call spinsert(?,?)"<br>result%=SQL Prepare(hstmt& sql$ Len(sq1($))<br>If result% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>正如看 到的 那样 这个 过 程按照 以前 在使 用 ODBC 游 标的示例 中看 到的 那种 模 式来分<br>配和准 备 ODBC 语句 主 要差别 是预 准备 的 SQL 语句 SQL Call 语 句用来 调用 存储 过 程<br>存储过 程的 名称 在 Call 关键 字之 后 然后 参数 标记 用于 由存 储过 程使 用的 全 部参数<br>提示 为了 执行 SQL Call 语句 ODBC 客户 程 序的 用 户必须 有该 存储 过程 的 EXECUTE<br>权限<br>要求绑 定参 数和 执行 该语句 的 Visual Basic 代码与 在 以前使 用 ODBC 预 准备语句 的代<br>码完全 一样<br>Dim lDep_ID As Long<br>Dim bDip_Name(25)As Byte<br>Dim lpLenDep_ID AS long<br>Dim lpLenDep_Name As long<br>''Assign the parameter Values<br>lDep_ID=0<br>lDep_ID=CLng(Text_DeptNumber.Text)<br> ''convert the string to a byte array for parameter binding<br>StringToByteArray bDep_nName,Text_DeptDesc.Text''Setup the binding lengths<br>lpSenDep_ID =4<br>lpLenDep_Name=25<br>''Bind the parameters<br>result%=SQLBindParameter(ByVsl hstmt&, 1, SQL_PARAM_INPUT, _<br> SQL_C_LING, SQL_INTEGER, 4, 0, lDep_ID, 4, lpLenDep_ID)<br>If result%<>SQL_SUCCESS Then<br> ''Error Handling<br>End if<br>result%=SQLBindParameter(ByVal hstmt&, 2, SQL_PARAM_INPUT, _<br> SQL_C_CHAR, SQL_CHAR, 25, 0, bDep_Name(0), 25, lpLenDep_Name)<br>If result%<>SQL_SUCCESS Then<br> ''Error Handling<br>End If<br> Result % = SQLExecute(ByVal hstmt&)<br> If result% <> SQL_SUCCESS Then<br> ''Error Handling<br>End If<br>绑定本 地变 量和 执行 SQL Call 语句的 ODBC 函数与 要求 执行 一个 标准 的预 准备 语句<br>完全一样 在前面的示 例中 可以看 到本地变量在代 码段的 开始 声明 重复一遍 位字 符<br>串用于 字符 串变 量中 得到 Visual Basic 的预 定位 置 来在内 存中 移动 字符 串变 量 接下 来<br>因为 spInsert 存储 过程 使用两 个 参数 所以 调用 函数 SQLBindParameter 两次 最后 当绑<br>定这些 参数 之后 当调 用 SLQExecute 函 数时实 际执行了 spInsert 存 储过程<br>5.8 ODBC 错误处理<br>报告 ODBC 错 误的 第一 个 位置是 在每 一个 不同 的 ODBC 函数生成 的 返回 代 码中 然而<br>返回代码本身并不能提供许多信息 相反 有关 ODBC 错误的主要附加信息源是<br>SQLGetDiagRec 函数 当 ODBC 函数调 用不 返回 值 SQL_SUCCESS(0) 时 ODBC 应用程 序<br>一般调 用 SQLGetDiagRec 函数 下面 的代 码说 明如 何 调用 SQLGetDiagRec 函数<br>Function DisplayODCError(hstmt&)As Integer<br> Dim sSqlState$<br> Dim sSqlErrorMsg$<br> Dim iSqlErrorMsgLen%<br> Dim lSqlErrorCode&<br> Dim sSqlErrorStr%<br> Dim iResult$<br> Dim iResponse%<br> sSqlState$ = String$(16, 0)<br> sSqlErrorMsg$ = String$(SQL_MAX_MESSAGE_LENGTH – 1, 0)<br> DoIRecNbr = iRecNbr + 1<br>iResult% = SQLGetDiagRec(SQL_HANDLE_STMT,hstmt&, iRecNbr%,_<br> sSqlState$, lSqlErrorCode&, _sSqlErrorMsg$, _<br> Len(sSQLerrorMsg$),isqlErrorMsgLen)<br>If iResult% = SQL_SUCCESS Or iResult%=SQL_SQL_SUCCESS_WITH_INFO Then<br> If iSqlErrorMsgLen%=0 Then<br> MsgBOX"No additional information",vbOKOnly<br> Else<br> MsgBox sSqlErrorStr$&Left$(sSqlErrorMsg$,_<br> iSqlErrorMsgLen%),vbOKOnly<br> End If<br> End If<br> Loop Until result<>SQL_SUCCESS<br>End Function<br>这个示例说明如何在 Visual Basic 中建立一个通用的 ODBC 错误处理函数<br>DisplayODBCError 函数 使用 SQLGetDiagRec 函数检 索与某 个给 定 的 ODBC 语句句柄相关<br>的全部 ODBC 错误 然 后 在消息 中显 示这 些错 误 这个 函数 依赖 于调 用路 径传送 到 相应 的<br>语句句 柄中<br>该函数 的第 一个 部分 声明在 函 数中 使用 的工 作变 量 接下来 使用 一个 Visual Basic 的<br>Do 循环 来执 行 SQLGetDiagRec 函数 直到 检索 出 全 部的错 误 正 如用户猜测的那样 某<br>个 给 定的 语句 句柄 可能 关 联 多个错误 条件 SQLGetDiagRec 函 数的第 一个 参数 是 一个整 数<br>它表示 SQLGetDiagRec 函数 将 要使 用的句 柄类 型 值 SQL_HANDLE_STMT 表 示 该函数将<br>使用一 个 ODBC 语句 句柄 第二 个参 数 是要使 用的 句 柄 第三 个参 数是 用作 输 入的一 个整<br>数 它 表示 要检索的 错误记 录 错误 记录 号 从 1 开始 如果 这个 参数 设置 为一 个 不存在 的<br>记录号值 那么 SQLGetDiagRec 函数 返回 值 SQL_NO_DATA 第四个参数是一个包含<br>SQLSTATE 代 码的输出 参数 第五个 参 数 是一个 指向输出 缓冲区的指针 它包含了 本地数<br>据源的错 误代码 第六 个参数是一个 包含错误文本的 输出字 符串 第七个参数是 一个输 入<br>参数 它 包含在第六个 参数中使用的 错误消息字符串 中可用 的字节数 最后 第 八个参 数<br>是一个 指向 缓冲 区的 指针 该 缓冲区 包含 在错 误消 息 字符串 中实 际返 回的 字节 数<br>只要 SQLGetDiagRec 函数返回 SQL_SUCCESS 那么该函数可以反复调用<br>SQL_NO_DATA 返回 代码 表示 没有 可用 的错 误记 录<br>5.9 小 结<br>ODBC 是一 种强大 而灵 活的 数据 访问 标准 在本 章中 介 绍了如 何为 SQL Server 建立<br>ODBC 数据 源 以及 如何 使用 ODBC API 执行 所有 的基 本数 据操 纵操作 借 助 这些信 息<br>就可以 准备 开始 使用 Visual Basic 建立自 己的 ODBC API 应 用程序 虽然编 写 ODBC 应用<br>程序 比使 用数据控 件和 活动数据 对象(ADO) 复杂 但是可以 获 得更 高性能 下 一 章我们将<br>介绍如 何用 ADO 访问 SQL Server第6 章 使用ADO 访问SQL Server 数据库<br>ADO 是基 于 OLE DB 基础 之上 用于 访问 OLE DB 兼 容数据 源 比如 SQL Server 2000<br>的数据 访问 接口 OLE DB 是一组 COM 接口 库 其 使得应 用程 序可 以访 问多 种 数据源<br>由于 ADO 使用 OLE DB 作为其 基础 它享 有 OLE DB 提 供的数据访 问体 系 结构 ADO 应<br>用开发 人员 无需 了解 如何编 写 COM 接口<br>6. 1 概 述<br>OLE DB 为 ODBC 的后 继者 考虑 到 ODBC 的 广 泛应用 使得 OLE DB 还有 许多 事 情<br>要做 ODBC 是基 于 SQL 的 它 可以 很好 地用 于关系型数据库访问 但 不 能用于非关 系型<br>数据源 像 ODBC 一样 OLE DB 也提 供了 对关 系型 数据 的访 问 但是 OLE DB 扩展了 由<br>ODBC 提供 的功能 OLE DB 主要用作 所有 数据 类型 的标 准 接口 除了 关系 型数据 库 的访<br>问外 OLE DB 还提 供了对 各 种各 样数据 源的 访问 包括像 Excel 电子 表 格 的列表数 据<br>像 dBase 的 ISAM 文件 电子邮 件 Windows 2000 的 Active Directory 和 IBM 主机的 DB2<br>数据 使用 OLE DB 用一 个 接口 就可以 访问 许多 不同 的数 据源<br>正如其 名称 所示 OLE DB 创建于 OLE 基础上 不像 ODBC 提 供了一种 DLL 调用级<br>接口 ADO 为 OLE DB 提供 了 COM 接口 此 接 口允许从其 他 OLE 兼 容的应用 程序 中调<br>用 OLE DB 是 Microsoft 的数 据访 问策 略( 称为通 用数据 访 问) 的基 础 通用数据访 问 指 的<br>是 一 组通 用接 口 它用来 代 表 来 自任何数 据源的 数 据 OLE DB 是使通用数据 访问 成为现<br>实的技 术 在数 据库 中所有 的 对象 维护的 理 念中 通 用数据 访问 和 OLE DB 各执一 端 创<br>建 OLE DB 时 不是 企图把 商 业要 求的所 有不 同 数据位移动到一个面 向对 象的 数 据库 中<br>而是 根 据 商 业 数 据 应在各 种 数据源中维护 这 一 理 解 创建的 OLE DB 提供 了针 对各 种数据<br>的类似 接口 OLE DB 可以 用 来访 问任何 可以 用基 本的 行和 列格 式表 示的 数据<br>6.1.1 OLE DB 体系 结构<br>使用OLE DB 的应 用程 序 一般 被 归类为 OLE DB 提 供者或 者 OLE DB 消费 者 图 6-1<br>表明了 OLE 提供 者和 OLE 消费者 之间 的关 系<br>正如看 到的 那样 OLE DB 消 费 者 就是为使 用 OLE DB 接口而编 写的应用程 序 相比<br>较而言 OLE DB 提供 者 负责访 问数 据 源 并且 通过 OLE DB 接口 向 OLE 消 费 者提供数据<br>进一步 划分 则有 两种 OLE DB 提供 者 数据 提供 者 和服务 提供 者 数 据提 供者简 单地 从<br>数据源中 提取数据 而 服务提供者则 传输和处理数据 服务 提供者一般提 供比较 高级的 功<br>能 来扩 展在 OLE DB 数 据提供 者中 的基 本数 据访 问 功能 Microsoft Query 是一个 OLE DB<br>服务提 供者 的示 例 而 SQL Server 的 Microsoft OLE DB 提供 者 是一 个数据提供者 的 样 例图6-1 OLE DB 消费者和提供者<br>正如希 望从 ODBC 源得 到 的那样 基于 不同 的 OLE DB 提 供者的功 能 OLE DB 提供<br>了不同 等级 的功 能 所有 OLE DB 驱动程 序支 持一 个 通用接 口 每 一个 驱动 程序可 以扩 展<br>OLE ODBC OLE DB OLE<br>功能 的基 本等 级 非常 类似于 每一 个不 同的 数 据源使 用 其自己 的<br>DB 提供 者 图 6-2 示意 了 如何要 求不 同的 OLE DB 提 供者访问 多个 数据 源 在 该图中 可<br>以看到 Visual Basic 应用 程序 如何 使用 OLE DB 访 问若 干个多机种数 据 源的高层概览 除<br>了 ODBC 数据 库 使 用不同 的 OLE DB 提供 者可 以访 问每 一个 不同 的数 据源 例如 可以<br>使用 SQL OLE DB Microsoft 的 SQL Server OLE DB 提 供者访 问 SQL Server 数据库 可以<br>OLE DB Microsoft Excel Exchange ODBC<br>分别使 用他 们的 提供者 访问 包含 在 或者 中的数 据<br>是一个 OLE DB 每 个数 据 源 提供者 这一 规则的 例外 为了提 供与 已有 的 ODBC 数 据 源最大<br>的兼容 性 Microsoft 开发了 MSDSSQL 这是 ODBC 的 OLE DB 提供 者 大多数的 OLE DB<br>提供者 提供 了直 接的 数据库 访 问 而 ODBC 的 MSDASQL OLE DB 提 供者使 用 已有的 ODBC<br>驱动程 序访 问数 据 ODBC 的 MSDASQL OLE DB 提 供者将 OLE DB 调用映射为等价的<br>ODBC ODBC OLE DB <br>调用 正如 用户 猜测的那 样 由这 个 的 提 供者 提 供的功 能依 赖于<br>基本的 ODBC 驱动 程序<br>图6-2 OLE DB 概览图每一个 OLE DB 提供 者通过 其显 示 的 COM 接口传送 数 据访问和反映其功 能 然而<br>OLE DB COM 接口 是一 种低级 接 口 要求 支持 指针 数据 结构 和直 接内 存分 配 因此 直<br>接使用OLE DB 提供 者不适 合 于不 支持低级 功 能( 如指针)的 开发环境 例如 Visual Basic<br>VBA VBScript Java JScript JavaScript 和其 他几 种语 言 但它适合于 ADO ADO 允许<br>通过交 互式 脚本 语言 访问 OLE DB 提供者 这些 脚本语 言 需要访问数据 但 是不支 持低级<br>内存访 问和 操纵<br>6.1.2 ADO ActiveX Data Objects<br>ADO 基本 上是 一个 OLE DB 消费者 它提 供了 对 OLE DB 数 据源的应 用程 序级 访问<br>ADO 还作 为许 多不 同的 Microsoft 开 发产品 Visual Studio Enterprise Edition 6 SQL Server<br>2000 等的 标准 组成 部分 提供<br>正如在 图 6-2 看到 的那 样 为了 访问 SQL Server 数据 OLE DB 提 供了两种 不同 的方<br>法 用于 SQL Server 和用于 ODBC 的 OLE DB 提供 者 ADO 可以使用这 两个 OLE DB 提<br>供者 它利用多层体系结构 这种体 系结 构使 用 ADO 从 基 本的网 络协 仪和 拓 朴 结构中 隔<br>离应用 程序 图 6-3 示意了 ADO OLEDB ODBC 和 PC 联 网 支 持之间的 关系<br>图6-3 ADO 使用的网络组件<br>在该图 的顶 端 可以 看到 Visual Basic ADO 应用 程序 Visual Basic 应 用程序 创 建和使<br>用各种 ADO 对象 ADO 对象框 架调 用相 应的 OLE DB 提供 者 如果 ADO 应 用 程序正在<br>使用 ODBC 的 OLE DB 提供 者 那么 将使 用 MSDASQL OLE DB 提供 者 如果 ADO 应用<br>程序使 用 SQL Server 的 OLE DB 提供 者 则使 用 SQLOLEDB 提供 者 当使 用 ODBC 的 OLE<br>DB 提供 者时 ADO 加载 文件 msdasql.dll 该文 件再加载 ODBC 驱动 管理 程序 ODBC 的<br>OLE DB 提供 者映 射由 ADO 产生的 OLE DB 调用 到 ODBC 调用 然后 ODBC 调用被传送<br>给 ODBC 驱动 管理 程序<br>ODBC 驱 动 管 理程序负责 加载 相 应的 ODBC 驱 动程序 ODBC 驱动程 序一 般 使 用一<br>种网络 进程 通信(IPC) 方法 例 如 命名管 道 TCP/IP 套 接字或 者 SPX 与提供 对 目标数 据访<br>问的远 程 IPC 服务 器通 信 SQL Server 的本地 OLE DB 提 供者不 使用任何 附加 的中 间层当使用 SQL Server 的 OLE DB 提供者 时 ADO 加载 sqloledb 文件 该文 件直 接 加载和 使<br>用与数 据库 通信 的相 应的网 络 IPC 方法 IPC 客 户机组件通过正在使 用的 联网 协 议创建 与<br>相应的 服务 器 IPC 通信 链接 网络 协议 负责 发送 和接收 通 过网 络的 IPC 数据 流 通用的网<br>络协议 包括 NetBEUI,TCP/IP 和 IPX 最后 在 这个 堆 栈的底 部是 物理 网络 拓扑 结构 物理<br>网络包 括实 际连 接网 络系统 的 适配 器卡 和电缆 线 Ethernet 和 Token Ring 是两 种最 常 用的<br>网络拓 扑<br>6.1.3 ADO 体系 结构<br>就像其 他几 个数 据访 问对象 模 型一 样 ADO 是使 用 层次对 象框 架实 现的 然而 ADO<br>对象模 型比 数据 访问 对象(DAO)或者 远程 数据 对象(RDO)框 架更简 单 在图 6-4 中 可以看<br>到 ADO 对象 层次 结构 的概览<br>在 ADO 对 象 模型中 Connection Recordset 和 Command 对象 是三 个 主 要的对象<br>Connection 对象 表示 对远 程数 据源 的连 接 除了创建对数据源的连接之外 Connection 对<br>象还可 以用 来控 制事 务范围 Connection 对 象可与 Recordset 对象 或者 Command 对象关 联<br>Recordset 对象 表示 从数 据源 返回 的结 果集 Recordset 对 象既可 以使 用一 个打 开 的 Connection<br>对象 也可 以 创 建它 自己对目 标数 据源 的 连 接 Recordset 对象允 许查 询和修改数 据 每一<br>个 Recordset 包含 一个 Field 对象集合 其中 每个 Field 对象表示 Recordset 中 的 一个数据<br>列 Command 对象可用 来执 行命 令和 参数化的 SQL 语句 可以用 于 SQL 语 句 和返回结 果<br>集的 SQL 查询 像 Recordset 对象 一样 Command 对象 既可 以使 用一 个活 动的 Connection<br>对象 也 可以 创建 它自 己 到目标 数据 源的 连接 Command 对象包 含一 个 Parameters 集合<br>在这个 集合 中的 每一 个 Parameter 对象表示 Command 对象使 用的 一个 参数 在 Command<br>对象执 行参 数化 的 SQL 语句的情 况 下 每一 个 Parameter 对象 表示 SQL 语 句中的一 个参 数<br>在 Connection 对象 的下 面是 Errors 集合 在 Errors 集合 中 每一 个 Error 对 象包含了<br>一个错 误信 息 该错 误是由 ADO 对象 框架 中的 一个 对象 碰到 的 除了 图 6-4 所 示 的主对象<br>之外 Connection Command Recordset 和 Field 对象都有 一个 Properties 集合 它是由 一<br>组 Property 对象组成 的 每一个 Property 对象都可以用 于 得到 或者 设置 与对 象相关 的 各种<br>属性<br>刚开始 看到 ADO 框架 时 觉得其 层次 结构 与 DAO 和 RDO 相同 其实 不然 与其他<br>数据访 问对 象框 架不 同 所有的 ADO 对象——除去 Errors Fields 和 Properties 对象——<br>都可 以创 建在 自己 身上 而 不需要访 问更 高一 层的 对 象 这就使 ADO 对 象框架 比其他 对<br>象模型 更简 单 更灵 活 例如 ADO 对象框 架允 许 打开的 访问 一个 Recordset 对象 而不<br>必首先 创建 一个 Connection 对象实 例 直接 使用 每一 个对 象而 不必 首先 调用 更 高层的 对象<br>的能 力使 ADO 比其 他对 象 框架使 用起 来更 加简 单 然而 正如将要在一些代码示例中看<br>到的那 样 ADO 并 非总 是 像 使用其 他框 架 那 样简单 明了图6-4 ADO 对象层次结构<br>ADO 是作 为一 个 OLE 自 动 服务器 创建 的 这 将更易于 从 Visual Basic 中访 问 ADO 函<br>数 使用 ODBC 或者 其他基 于 DLL 的 API 时 必须 在.bas 或者.cls 模 块中手工 声明 函数 和<br>参数 而使 用 ADO 只需 要 在项目 中增 加 对 ADO 的引 用 这些将在下 一节 解释 当在 Visual<br>Basic 开发 环境 中增 加 ADO 的引用 之后 就 可以使 用 所有的 ADO 对象 在 Visual Basic 中<br>ADO<br>使用 所需 的步 骤总 结 如下<br>1. 在 Visual Basic 中 添加 对 Microsoft ADO 2.x 对 象 库的引 用<br>2. 使用 Connection Command 或者 Recordset 对象 打开 一个 连接<br>3. 使用 Command 或者 Recordset 对象 访问 数据<br>4. 关闭到 Connection Command 或者 Recordset 对象 的连 接<br>6.1.4 ADO 文件 相关 清单<br>表 6-1 汇总 了ADO 文件 相 关清单<br>目录 文件 描述<br>C:\Program Files\Common Files\System\ADO 所有 实现 ADO 对象的文件<br>实现 SQL OLE DB 提供者的动态链接库<br>C:\Program Files\Common Files\System\OLE DB Sqloledb.dll<br>C:\Program Files\Microsoft SQL Server\80\ Sqloledb.h 开发 SQL OLE DB 消费者的 C/C++ 头<br>Tools\Devtools\Include 文件<br>开发 Microsoft Visual Basic 应用程序的<br>C:\Program Files\Common Files\System\OLE DB Sqloledb.rll<br>SQL OLE DB 资源文件<br>实现 MSDASQL 提供者的动态链接库<br>C:\Program Files\Common Files\System\OLE DB Msdasql.dll<br>C:\Program Files\Microsoft SQL Server\ Msdasql.h 开发 MSDASQL 消费者 的 C/C++ 头文<br>80\Tools\Devtools\Include 件<br>ADO 范例程序<br>C:\Program Files\Microsoft SQL Server\ ALL<br>80\Tools\Devtools\Samples\Ado<br>6.1.5 在Visual Basic 中 增加对 ADO 的引用<br>在从Visual Basic 中使 用 ADO 之前 必须 设置 对 ADO 类 型库的 引用 ADO 类型库也称为 ADO 自动 服务 器 当 第一次 从 Microsoft Web 站 点中下载 ADO 支 持时或者 当安 装了<br>包含 ADO 的一 种产 品( 这些 产品列 在前 面一 节中) 时 提供对 ADO 2.x 基 本支持 的文件 安装<br>在系统 上 然而 在 开始在 Visual Basic 项目 中使 用 ADO 之前 需要 在 Visual Basic 的开<br>发环境 中设 置对 ADO OLE 类型 库的引 用 为了 在 Visual Basic 5 或者 6 中增 加 对 ADO Objects<br>2.6 Library 的引用 启动 Visual Basic 然后 选择 Project| References 则显 示 References 对<br>话框 如图 6-5 所示 本 章后面 的内 容 以 ADO Objects 2.6 Library 为例进行 介绍<br>图6-5 设置对 ADO Objects 2.6 Library 的引用<br>在 References 对话 框中 滚动 Available References 列表 直到 看到 Microsoft ActiveX<br>Data Objects 2.6 Library 选项 单击 这个 复选 框 然 后单击 OK 按钮 则将 ADO Objects Lirary<br>增加到 Visual Basic 的 IDE 中 与 ActiveX 控件 不同 在 Visual Basic 的 IDE 中 增 加一个引<br>用不会 在 Visual Basic 的 Toolbox 中创 建任 何可 视的 对 象 为了 看到 ADO 对象 属性 和方<br>法 需要 使用 Visual Basic 的 对象浏 览器 图 6-6 使用 Visual Basic 的对象 浏 览 器显示了 ADO<br>Objects Library<br>图6-6 从对象浏览器中查看 ADO 类6.2 使用 ADO 连接 到 SQL Server<br>在 Visual Basic 开发 环境 中增 加了 对 ADO 2.6 Library 的引用之后 就可以准备 使 用 ADO<br>创建Visual Basic 应用 程序 不像 DAO 或者 RDO 对 象模型 ADO 不用在创 建 对 数据 源的<br>连接之 前必 须创 建的 顶层对 象 使用 ADO 应用 程 序执行 的第 一步 操作 是使 用 Connection<br>Command 或者 Recordset 对象 打开 一个 连接 ADO 可以使用 ODBC 的 OLE DB 提供者<br>MSDASQL 或者 SQL Server 的 OLE DB 提 供者连 接到 SQL Server MSDASQL 提供者允<br>许 ADO 对象 框架 用于 已有的 ODBC 驱动 程序 而 SQL OLE DB 提 供者直 接连 接到 SQL<br>Server 这两 个 OLE DB 提供 者都 可以 用于 ADO Connection Command 和 Recordset 对象<br>在下一 节中 将 介绍 如何使 用 ODBC 的 OLE DB 提 供者和 SQL Server 的 OLE DB 提供者<br>创建 一个 与 SQL Server 的连接 还将介绍如何使用 ADO Connection 对 象以及直 接通 过<br>Recordset 对象 连接 到 SQL Server<br>6.2.1 使用 ODBC 的OLE DB 提供者 打开 一个 连接<br>如果熟 悉 DAO 或者 RDO 对象框 架 可以 使用 ADO Connection 对象 和 ODBC 的 OLE DB<br>提供者 创建 一个 对 SQL Server 系统的 连接 这可 能是 开始 创建 ADO 应 用程序的 起点 像<br>DAO 和 RDO 一样 ODBC 的 OLE DB 提供者 MSDASQL 使用一 个 ODBC 驱 动 程序访问 SQL<br>Server 这意 味着 运行 应用程 序 的系 统要 么必须 有一个用于 SQL Server 的 ODBC 驱动程 序<br>和一个 在 ODBC Administrator 中用于 SQL Server 的 Data Source Name(DSN) 要 么应用程<br>序必须 使用 一个 无 DSN 的连 接字 符串<br>下面的 代码 说明 如何 使用 ADO Connection 对象 和 MSDASQL 提供者提示 用 户选择 一<br>个将要 用于 连接 SQL Server 的已有的 DSN<br>Dim cn As New ADODB.Connection<br>''DSN Connection using the OLE DB provider for ODBC-MSDASQL<br>cn.ConnectionString= " _<br>DATABASE=pubs;UID=_<br>sLoginID _<br>;PWD= sPassword”<br>''Prompt the user to select the DSN<br>cn.Properties("Prompt") = adPromptComplete<br>cn.Open<br>cn.Close<br>在这个 代码 示例 的开 头 可以看 到创 建了 一个 ADO Connection 对象 的 新实例 cn 由<br>于 ADO 对象 不依 赖上 层对象 所以 每一 个对 象通 常必须 有 一个 使用 Visual Basic 的 New 关<br>健字的 Dim 语句 接下 来 Connection 对象 cn 的 ConnectionString 属性赋予了一个 ODBC<br>连接字 符串 像正 常的 ODBC 连接字 符串 一样 这个用 在 ADO 的 Connection String 属性<br>中的连接 字符串必须包 含一组预定义 的关键字 其中 每一个 关键字和与其 相关的 值使用 分<br>号分开 因为 ADO 基于 OLE DB 而不仅 是 ODBC 所以在连 接字 符串 中使 用的 这些 关键<br>字与在 标准 的 ODBC 连 接 字符串 中使 用的 关键 字有 点 儿不 同 表 6-2 提 供了有 效的 OLE DB<br>连接字 符串 关键 字表6-2 OLE DB 连接字符串的关键字<br>关键字 描述<br>OLE DB <br>PROVIDER 这个可选的关键字可以用来指定将要使用的 提供者的名称 如果没有提<br>供提供者的名称 那么该连接将使用 MSDASQL 提供者<br>DSN 由 ODBC Administrator 创建的已经存在的数据源名称<br>一个已经存在的 ODBC 驱动程序的名称<br>DRIVER<br>SERVER 一个已经存在的 SQL Server 系统的名称<br>ID<br>UID 用于数据源的登录帐号<br>PWD 与该登录帐号 ID 相关的口令<br>DATABASE SQL Server 的目标数据库名称<br>当 OLE DB 连接 字符 串赋予 Connection 对象的 ConnectionString 属 性之后 该 Connection<br>对象的 Prompt 属性 赋予 adPromptComplete 常量值 该值 指定 ODBC 驱 动管理 程序 提示 任<br>何没有 在连 接字 符串 中提供 的需 求的 连 接信 息<br>Prompt 属性控制 ODBC 驱动 管理 程 序 如何 回应 包含 在连 接 字 符串 中的 关键 字和 值<br>表 6-3 列出 了用 于 Prompt 属性 的有 效值<br>表6-3 ADO MSDASQL Prompt 常量<br>常量 描述<br>AdPromptNever ODBC 驱动管理程序只能使用由连接字符串提供的信息建立连接<br>如果没有提供足够的信息 那么连接失败<br>abPromptAlways ODBC 驱动管理程序总是显示 ODBC Administrator 以提示输入连接<br>信息<br>abPromptComplete ODBC 驱动程序确定所有需要的连接信息是否都提供在连接字符串<br>中 如 果提供 了所 有必要 的信 息 就 不必 进一步 提示 建立 连接<br>如果 丢失 了任 何必 要的 信息 ODBC Administrator 提示用户输入丢<br>失的信息<br>AbPromptCompleteRequired 该选项像 adPromptComplete 一样 但禁止提示任何已经提供的信息<br>提示 ADO Connection Command 和 Recordset 对象 的 Properties 集合允许 在 属 性集<br>合 中 使用 命名 的条 目得 到 和设置属性 值 事实 上 有些 像 Prompt 属性 的 ADO 属<br>性没有 直接 通过 对象 框架显 示 并且 只 能通过 Properties 集 合访问 这个动态<br>的Properties 集合 为 ADO 对象模型 提供 了 比 DAO 或者 RDO 更 大的灵 活性 它还<br>可隐藏 属性 使其 比较 为直接 的 DAO 和 RDO 对 象模 型更难 找 到和使 用 如果不能<br>找到一 个认 为应 该存 在的 ADO 属性 试着 通过 迭 代 Properties 集 合来找到这 个<br>属性<br>在这个 示例 中 这 个连 接 字符串 没有 使用 PROVIDER 关键 字 所以 在默 认情况 下<br>使用 ODBC 的 OLE DB 提供 者 MSDASQL 这就 意味着与 SQL Server 的连接是通过一 个<br>ODBC 驱动 程序建 立的 另外 连接 字符 串没 有为 DSN 关 键字指 定值 这就 意味着这个 连<br>接字符 串必 须使 用 DRIVER 关键字 建立 一个 无 DSN 连接 或者为建立一个 到 SQL Server<br>的连接 ODBC 驱 动管 理 程序必 须提 示用 户输 入 Data Source Name(DSN) 在这 个示例 中<br>没有使 用 DRIVER 关键 字 值 adPromptComplete 在 Prompt 属性中 指定 这 就允许 ODBC 驱<br>动管理 程序 提示 用户 选择一 个 已经 存在 的 ODBC 数据 源<br>当用户 完成 了 ODBC 驱 动 管理程 序的 提示 之后 Connection 对象 cn 的 Open 方法连 接<br>到 SQL Server 这个 Connection 对象的 Open 方法带 三个 可选 的参 数<br> 第一 个可 选的 参数 接受 包 含一个 OLE DB 连接字符 串 的字符 串 该参 数执 行与<br>Connection 对象 的 ConnectionString 属性 完全 相同 的 功能 可以 使用 这个 参数 作 为<br>设置 ConnectionString 属 性的另 一 种方法<br> 第二个可选参 数接收一个字符串变量 它包含 一个用 于目标数据源 的有效 的登录<br>帐号 ID<br> 第三个 可选 参数 接收 一个字 符 串变 量 它 包含 一个 用 于该目 标数 据源 的口 令<br>提示 OLE DB 连接 字符 串和 Open 方 法 的第 二个 和第三 个参数 都允 许指 定登 录信息<br>但是不 要同 时使 用这 两种方 式 因为 正常 情况 下 需 要使用 OLE DB 连 接字符串<br>提供 OLE DB 提供 者的 名称 所以 为了 简单 起见 常 常 只 提供登录信 息作 为 OLE DB<br>连接字 符串 的一 部分<br>在这个 示例 中 没有 其他进 程 所以 用 Close 方法终 止连 接<br>6.2.2 使用 ODBC 的OLE DB 提供者 打开 一个 无 DSN 连接<br>前面的 示例 说明 了如 何使用 MSDASQL 提供 者和 一个已 有 的 DSN 创 建一个 SQL Server<br>连接 然而 在一 些情 况 下 应 用 程序需 要 建立一 个基 于 ODBC 的连 接 并 且 它不能 依靠<br>一个预 配置 的 DSN MSDASQL OLE DB 提供者 也支 持使 用无 DSN 连接 使用无 DSN 连<br>接不需 要已 有的 数据 源<br>下面的 代码 说明 如何 使用 ADO Connection 对象 和 MSDASQL 提供者建 立一个 到 SQL<br>Server 的无 DSN 连接<br>Dim cn As New ADODB.Connection<br>DSNless Connection using the OLE DB provider for ODBC-MSDASSQL<br>cn.ConnectionString = "DRIVER=SQL Server" & _<br> ";SERVER=" & sServer & _<br> ";UID=" & sLoginID & _<br> ";PWD=" & sPassword & _<br> "; DATABASE=pubs"<br>cn.Open<br>cn.Close<br>上面代 码第 一步 是创 建一个 新 ADO Connection 对象 cn 接下来 为 Connection 对象 cn<br>的 ConnectionString 属 性赋 予 了一个 连接 字符 串 因为 这 个连 接字 符串表示企图创 建一个无<br>DSN 连接 所 以 它 与在 前面示例 中提 供 的连 接字 符串 不 同 因为 没有 使用 PROVIDER 关<br>键字 所以 在默 认情 况下 使用 ODBC 的 MSDASQL 提供者 正 如 用户可能 猜 测的那样<br>不需要 DSN 关键 字来 创建 一个 无 DSN 连接 相反 DRIVER 关 键字的 值是“SQL Server”<br>表示应 该使 用 SQL Server ODBC 驱动 程序<br>注意 虽然 DRIVER 关键 字使 用的 值可 以放 在括 号中 例如{SQL Server} 但是 这不 是必要的<br>除了指 定将 要使 用的 ODBC 驱动程序 之外 一个 无 DSN 的 ODBC 连接 字 符 串必须 表<br>示将要 使用 的服 务器 和数据 库 这些 值由 SERVER 和 DATABASE 关 键字提供 最后 在<br>表 6-2 中描 述的 UID 和 PWD 关键字 提供 必要 的 SQL Server 登录信 息<br>使用无 DSN 连接 字符 串设 置 ConnectionString 属 性之后 Connection 对象 的 Open 方<br>法启动 一个 对 SQL Server 系统的 连接 然后 Connection 对象 的 Close 方法终止该连接<br>6.2.3 使用 SQL Server 的OLE DB 提供 者 打 开一个 连接<br>当没有 可用 的本 地 OLE DB 提供者 时 ODBC OLE DB 提 供者主 要是允许 ADO 应用<br>程序访 问 ODBC 兼容 的数据 库 ODBC 当然是 已建立 的 数 据库访问 标准 并且由各种流 行<br>的数据 库支 持 而 OLE DB 不是这样 它是一 门 新技术 几乎 没有 那多 本地 的 OLE DB 提<br>供者可 供使 用 并 且它 们 大多数 来自 Microsoft SQL Server 7 是最早有本 地 的 OLE DB 提<br>供者的 数据 库之 一 SQL Server 7 包括了由 文件 sqloledb.dll 提供 的用于 SQL Server 的 OLE<br>DB 提供 者<br>使用 SQL Server 的 OLE DB 提供者 非常类 似于 使用 ODBC 的 OLE DB 提供 者 因为 SQL<br>Server 的 OLE DB 不使 用 ODBC 所以 没有 必要 使用 一个 数据 源或 者一 个已 经存 在的 ODBC<br>驱动程 序 然而 必 须指定 OLE DB 提供 者 的名称<br>下面的 示例 说明 如何 使用 ADO Connection 对象 和 SQL Server 的 OLE DB 提供 者 建 立一个<br>对 SQL Server 的连接<br>Dim cn As New ADODB.Connection<br>Connect using the OLE DB provider for SQL Server - SQLOLEDB<br>cn.ConnectionSting =" PROVIDER=SQLOLEDB" & _<br> "; SERVER=" & sServer & _<br> "; UID=" & sLoginID & _<br> "; PWD=" & sPassword & _<br> "; DATABASE=pubs"<br>cn.Open<br>Perform processing here<br>cn.Close<br>上面代 码与 前面 的那 些示例 一 样 创建 了一 个 ADO Connection 对 象的实例 然后 为<br>ADO Connection 对象 的 ConnectionString 属性赋予了一 个 OLE DB 连 接字符 串 该连 接字<br>符串使 用 PROVIDER 关键 字指 定使 用的 SQL OLE DB 提供 者 为了使 用 SQL Server 的 OLE<br>DB 提供 者 必须 指定 PROVIDER 关键字 如 果省 略 了这个 关键 字 那 么默 认的提供者是<br>MSDASQL 另外 还需 要 SERVER 和 DATABASE 关键 字 SERVER 关 键字指 定将要 连<br>接的 SQL Server 系统名 称 DATABASE 关键字 确认 将要 使用 的数 据库 UID 和 PWD 关键<br>字提供 了登 录到 SQL Server 所需的认 证值<br>当设置 ConnectionString 属性 之后 Open 方法 启动 这 个连接 一旦 建立 了连 接 就可<br>以执 行其它数 据库的访 问 在这 个 示 例中 没 有附加 的工作 所以 使用 Close 方法 关闭连<br>接6.2.4 终止 一个 连接<br>像前面 示例 说明 的那 样 在终止 应用 程序 之前 应该使 用 Connection 对象 的 Close 方<br>法终止 该数 据库 连接 Close 方法 的一 个示 例如 下<br>Dim cn As New ADODB.Connection<br>Perform work with the connect and then end it<br>cn.Close<br>6.3 使用 Recordset 对象查 询 SQL Server 数据 库<br>ADO 允许 使用 Recordset 或者 Command 对象检索 数据 这两 种对 象都 可以 用于 一个<br>活动的 Connection 对象 或者 打开 它 们自己 的连 接<br>6.3.1 Recordset 分类<br>Recordset 对象 表示 一个 从数 据库 查询 中返 回的 结果 集 Recordset 对 象支持几 种 不同类<br>型的游 标 它们与不 同 类型 的 ODBC 游标 相对 应 ADO 支 持只向 前 静态 键 集和动 态<br>的 Recordset 对象 在 Recordset 打开之 前 必须 设置 在 Recordset 对象 中使 用的游 标 类型<br>如果没 有指 定希 望使 用的 Recordset 对象 类型 那么 ADO 自 动使用 一个 只向 前 游标<br>只向前游标 只向前游标 只向前游标 只向前游标 在缺 省情 况 下 ADO 使用 只向 前游 标 在 所有的 ADO 游 标类型中 该<br>游标提供了最好的性能和最低的开支 然而 它的功能也是最低的 使用只向前游标的<br>Recordset 对象 是可 修 改 的 但是 只 能 修改 当前 行 其他 用户 在 基 表中所作的任 何 变化 都 不<br>反映在 Recordset 对象 中<br>静态游标 静态游标 静态 游标 提供 了游 标打 开时 数据 的快 照 使 用静 态游标的 Recordset 对象是<br>静态游标 静态游标<br>不可修改 的 并且它们 不反映基表中 的任何变化 除 非该游 标关闭 并重新打开 由于其 静<br>态性质 所以 由静 态的 游 标创建 的 Recordset 对象 一 般比使 用键 集或 者动 态游 标 的 Recordset<br>对象的资 源强度低 然而 由于静态 游标制作了数据 的本地 拷贝 所以对 于大规 模的结 果<br>集 在使 用这 种游 标时 要 小心<br>键集 键集游标 游 标 键集游标创建了一组本地键 其中每一个 键是 结果 集内一 行的一个索引<br>键集 键集 游 游 标 标<br>当应用 程序 访问 使用 键集游 标 的 Recordset 对象时 来自本 地键 集的 键 值 从基表 中检索 相应<br>的行 使用 键集 游标 的 Recordset 对象是 可修 改的 但 是 当它们完全填满之 后 就不能动 态<br>地 反 映其 他用 户在 基表 中 所 作的变化 键集游标有很 强大的功能 但是他们相对 来说资 源<br>也是紧 张的 因 为客 户机系 统 必须 维护 整个 结果 集的关 键 值以 及包含实际数据值 的缓存 区<br>动态游标 动态游标 动态 游标 是功 能 最强大 的一 种 ADO 游标 但 是 它 们的资 源 也 是最紧张的<br>动态游标 动态游标<br>动态游标 非常类似于键 集游标 它们都使用与结果集 中每一 行相对应的一 组本地 键 并且<br>都是可 修改 的 然而 不 像使用 键集 游标 的 Recordset 对象 使用 动态 游标 的 Recordset 对<br>象可以自 动反映其他应 用程序在基表 中所作的变化 为 了 动 态 地 维护结果 集 使 用动态 游<br>标的 Recordset 对象 必须 连续 地刷 新结 果集 使用 任 何变化 自动 修改 本地 结果 集 显然 这<br>是一个 紧张 的进 程6.3.2 使用 只向 前的 Recordset 对象<br>Recordset 对象 可以 用于 一个 已经 存在 的 Connection 对象 也可 以打 开一 个对 目 标数据<br>源的连 接<br>提示 当 Recordset 对 象 打开其 自己 的 Connection 对象 时 ADO 对 象框架 自动 创建 一<br>个 Connection 对象 但是 这个对象 与 Visual Basic 的 程序变量 无关 这使得<br>使用 Recordset 对 象 更快更简 单 但是 它也增加 了 要求 为 每 一 个新 Recordset<br>对象创 建 Connection 对象 的开支 如果 应用 程序 要 创建多 个使 用相 同数 据库 的<br>Recordset 对象 那么 可 以更有效地 使用 Connection 对象 然后 使用 已经 存在<br>的Connection 对 象关 联每 一个新的 Recordset 对象<br>下面的 代码 清单 说明 如何使 用 Recordset 对象 和 ADO Connection 对象<br>Private Sub ForwardOnlyRecordset(cn As ADODB.Connection)<br> Dim rs As New ADODB.Recordset<br> Screen.MousePointer = vbHourglass<br> Associate the Recordset with the Connection object named cn<br> rs.ActiveConnection = cn<br> Use the open method<br> rs.Open"Select From stores",,,,adCmdText<br> DisplayForwardGrid rs,Grid<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>在使用 Recordset 对象 之前 需要 把它 赋给 一个 Visual Basic 变量 在这 个子例程开 头<br>的 Dim 语句 创建 了一 个新 Recordset 对象 rs 接下 来 把 Recordset 对象 rs 的 ActiveConnection<br>属性设 置为 一个 活动 的 Connection 对象 必须 创建 该 Connection 对象并连接 到 SQL Server<br>就像在 前面 示例 中的 那样 ADO Connection 对 象 可以使 用 ODBC 的 OLE DB 提供者或者<br>使用 SQL Server 的 OLE DB 提供 者 这两 个 OLE DB 提供 者 的 ADO 代 码是相 同的 把一<br>个活动 的 Connection 对 象赋给 rs 对象 的 ActiveConnection 属性 就等 于 把新的 Recordset<br>对象和 连接 的 SQL Server 系统关 联<br>当设置 ActiveConnection 属性 之后 使用 Recordset 对象 的 Open 方法 打 开一 个只向前<br>游标 这个 Recordset 对象 的 Open 方法 带五 个可 选的 参数<br>第一个 参数 是一 个 Va r i a n t 数据 类型 就 像用 户认 为的那样 它可 以接 受许 多不 同的 值<br>——例如 一个 已经 存在 的 Command 对象 的名称 一条 SQL 语句 一个 表名 或者一 个 存储<br>过程的 名称 在 前面 的示例 中 第一 个参 数包 含了 一 条简单 的 SQL Select 语句 该语 句将<br>创建一 个结 果集 这个 结 果集由 包含 在 存储 表中的 全 部行和 列组 成<br>Open 方法 的第 二个 可选 参 数可以 用 于关 联 Recordset 对 象和一 个 ADO Connection 对象 这个 参数 执行 的功 能 与 Recordset 对象的 ActiveConnection 属性完全 相 同 并且 可以 使<br>用这 个参 数作 为 设 置 ActiveConnection 属性 的 另 一种 方法 这个参 数既 可 以 接受 一个 包含<br>OLE DB 连接 字符 串的 字 符串 也可 以接受 一个 包含 活动 的 ADO Connection 对象名的<br>Va r i a n t 如果 指定 一个 OLE DB 连接字 符串 而不是 一个 Connection 对 象的名 称 那么 ADO<br>将隐含 地创 建一 个 Connection 对象 并且 使用 它建 立一 个到 目标 数据 源的 链接<br>Open 方法 的第 三个 可选 参 数指定 Recordset 对象 将要 使用 的游 标类 型 如果 没有 使用<br>这个参数 那么在缺省 情况下 游标 类型设置为只向 前类型 这是最简单 也是性 能最好 的<br>选项 表 6-4 提供 了一 些 ADO 常量 它 们用来指 定 Recordset 对象 将要 使用 的游标 类 型<br>表 6-4 Recordset 游标类型<br>ADO 常量 游标类型<br>adOpenForwardOnly 只向前游标( 默认)<br>adOpenStatic 静态游标<br>键集游标<br>adOpenKeyset<br>adOpenDynamic 动态游标<br>第四个 可选 参数 指定 OLE DB 提供者 将要 使用 的 锁定 类 型 如果 没有 使用 这个 参 数<br>那么在 默认 情 况 下 该 锁 定 的类型设 置为 只读 表 6-5 提供 了 ADO 常量 它们用来 指定<br>Recordset 对象 将要 使用 的锁定类 型<br>表 6-5 Recordset 锁定类型<br>锁定类型 描述<br>adLockReadOnly 只读( 默认)<br>adLockPessimistic 保密锁定<br>adLockOptimistic 优化锁定<br>adLockBatchOptimistic 使用批模式修改的优化锁定<br>第五个 可选 参数 指定 Open 方法的 选项 这些 选项 参数 明确 地告 诉 ADO 如果第 一个<br>参数没 有包 含 Command 对象的 名称 该如 何处 理第一 个 参数<br>提示 这似 乎是 有些乏味 但 指定第五个 参数 的值 可 以提高 性 能 因为 ADO 不需要测<br>试数据 源来 确定 在 Open 方 法的第 一个参 数中 提供 了 哪一种 类型 的值 然而 如<br>果第五 个参 数 指 定了 一个 常 量 它与 第一 个参 数提 供 的值不 匹 配 那么 ADO 将<br>生成一 个错 误<br>表 6-6 提供 了 ADO 常量 它 们用来 指定 将由 Recordset 对 象使用 的选 项<br>表 6-6 Recordset 选项<br>选项 描述<br>源是未知的 ADO 必须测试源( 默认)<br>adCmdUnknown<br>源是一个文件名称<br>adCmdFile<br>源是一个存储进程的名称<br>adCmdStoredProc<br>源是一个表名称<br>adCmdTable<br>源是一个命令( 或者 SQL 语句)<br>adCmdText当 Open 方法完成之后 可 以处理 Recordset 对象 中的 数据 在前面的示例中 调用<br>DisplayForwardGrid 子例 程在网格中 显 示 Recordset 对象 rs 的内 容 在这 个代 码 的下一 段<br>将看到 如何 在 Recordset 对象的 行中 移 动 以及 如何访 问 在 Fields 集合中的列信息 Display-<br>ForwardGrid 子例 程如 下<br>Private Sub DisplayForwardGrid _<br> (rs As ADODB.Recordset, Grid As MSFlexGrid)<br> Dim fld As ADODB.Field<br> Set up the grid<br> Grid.Cols = rs.Fields.Count<br> Grid.Rows = 1<br> Grid.Row = 0<br> Grid.Col = 0<br>Set up the Grid headings<br>For Each fld In rs.Fields<br> Grid.ColWidth(Grid.Col) = _<br> TextWidth(String(fld.ActualSize + 4, "a"))<br> Grid.ColAlignment(Grid.Col) = 1<br> Grid.Text = fld.Name<br> If Grid.Col < rs.Fields.Cunt - 1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br>Next fld<br>Move through each row in the record set<br>Do Until rs.EOF<br> Set the position in the grid<br> Grid.Rows =Grid.Rows + 1<br> Grid.Rows = Grid.Rows - 1<br> Grid.Col = 0<br> Loop through all fields<br> For Each fld In rs.Fields<br> Grid.Text = fld.Value<br> If Grid.Col < rs.Fields.Count-1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br> Next fld<br> rs.MoveNext<br> Loop<br>End Sub在这个 子例 程的 开头 可 以看到 一个 Recordset 对 象的实 例 rs 作 为第一 个参 数传 送<br>MSFlexGrid 对象的 实例 作为 DisplayRSGrid 子 例程的 第二 个参 数传 送 这样 就允 许用 许多<br>不同的 Recordset 和 Grid 对象重 复 使用同 一个 子 例程 在 这个子例程 中 的 Dim 语 句 创建了<br>一个 ADO Field 对象的实例 fld<br>注意 不像 前面 的那 些 ADO 示例 没必 要使 用 New 关 键字声 明 Recordset 对象 或者 ADO<br>Field 对象 因为 这两 个 变量都引用 已经 创建 的 Recordset 对 象的实 例<br>当 ADO 对象 声明 以后 子例 程 DisplayForwardGrid 的下 一部 分建 立显 示 Recordset 对<br>象的网 格 首先 网格 的 列数由 Recordset 对象 中 Fields 集合的 Count 属 性设置 其次 设<br>置网格 的 Row 属性 使它 至 少有一 行包 含列 标题 信息 然后 使用 网格 的 Row 和 Col 属性<br>设置当 前网 格单 元在 第 0 行第 0 列(网格的左上 角)<br>一旦网格 的初始行 数和列 数设置之后 建 立每一个 网格列的 标题值和大小 结果集 中<br>的每一 列都 在 Recordset 对象 的 Fields 集合中有 一个对 应的 Field 对象 一个 For Each 循环<br>迭代包 含在 Fields 集合中的 全 部 Field 对象 在 For Each 循环 中 第一 个动 作是 使用 网格<br>的 ColWidth 属性 设置网 格的 列宽 为了 设置 合适 的 列宽 ColWidth 属性 要 求有网格的 Col<br>属性提 供的 当前 网格 列的索 引 ColWidth 属 性 必须 赋予一 个值(以 缇为单位 一个 缇是一个<br>打印机 点的 二十 分之 一) 使用 Visual Basic 的 TextWidth 函数返回每 一 个 Field 对象所需的<br>缇数<br>正确的 缇数 可以 这样 确定 使用 Field 对象的 ActualSize 属性 创 建一 个占位 符字符串<br>再加上四 个额外的字符 这样 有助于防止网格列过分 拥挤 接下 来 该网 格的 每 一个列 的<br>ColAlignment 属性 设置 为左 对齐的 单 元格文 本 然后 使用 Field 对象的 Name 属 性 作为该<br>网格列 的标 题文 本 最后 使用 网格 的 Col 属性增 加 当前列 If 语句确 保 Col 属 性 没有赋<br>予比最 大的 网格 列数 还大的 值<br>注意 因为 ADO 对像 框架不像 DAO 和 RDO 框架 那样 支 持 OrdinalPosition 属性 所以<br>必 须 增加 代码 手动 跟踪 当 前列的位置<br>接下来 Do Until 循环读取 Recordset 对象 中的 全部 列 Do Until 循环一直进行到<br>Recordset 的 EOF( 文件结尾)属性变 成真——它表示 已 经读取 Recordset 中的 全部行 在 Do<br>Until 循环 内 增大 该网 格的 Row 属性来 扩大 网格 的 尺寸 增加 Row 属性将当前位置移动<br>到新的 网格 行 然后 将 当前网 格列 设置 为第 一列 For Each 循环将包含 在 Fields 集合中<br>的数 据值 移动 到网 格列 再 强调一下 If 测 试确保该 代码 不能 访问 一个 无效 的网 格列 当<br>Field 的全部值 都处 理完 之后 Recordset 对象 的 MoveNext 方 法移动游 标到 Recordset 对象<br>的下一 行<br>6.3.3 使用 键集 Recordset 对象<br>前面 的代 码示 例说 明如 何 使用 ADO 创建一个使用只向前游标的简单的 Recordset 对<br>象 只向 前游标速度快 效率 高 然而 它没有其他 类型的 游标功能强大 例如 只向 前<br>游标只 能按 照向 前顺 序一个 个 地传 送 Recordset 而 键 集游标允 许多 个传 送 以及 前后 滚动<br>下面的 代码 说明 如何 使用一 个 Recordset 对象 该 对 象使用 一个 键集 游标<br>Private Sub KeysetRecordset(cn As ADODB.Connection) Dim rs As New ADODB.Recordset<br> Screen.MousePointer = vbHourglass<br> Associate the Recordset with the open connection<br> rs.ActiveConnection = cn<br> rs.Source = "Select From employee"<br> Pass the Open method the SQL and Recordset type parameters<br> rs.Open , , adOpenKeyset, adLockReadOnly<br> '' Display the grid -- use a 1 to display in forward order<br> DisplayKeysetGrid rs, Grid, 1<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>在这个示例中 创建了一个新的 Recordset 对象 rs 然后 Recordset 对象 rs 的<br>ActiveConnection 属性 设置为 cn 它是 已有 的带 有一 个活 动的 数据 库 连接的 ADO Connecton<br>对象的 名称 接下 来 Recordset 对象的 Source 属 性赋予了一个简单的 SQL Select 语句<br>该语句 将从 employee 表中返回 全部 行和 列<br>注意 本 章中 的几 个示 例 使用了 简单 的未 限定 的 SQL Select 语句 然而 除非知 道目<br>标表相 对较 小 否则 使用 SQL Select 语句 的 Where 子句 可 使 自己的结果 集<br>尽可能 的小<br>接下来 Open 方 法在 目标数 据 库上执 行源 SQL 语句 在这 个示 例中 Open 方法的 前<br>两个参 数不 需要 指定 因 为 它 们已经使用 Recordset 对象的 Source 和 ActiveConnection 属性<br>设置了 在第 三个 参数 中 的 adOpenKeyset 值表示这个 Recordset 对 象将使 用一 个 键集游 标<br>在第四 个参 数中 的 adLockReadOnly 值使这个 Recordset 为只 读<br>当 Open 方法 执行 了该 查询之 后 DisplayKeysetGrid 子 例 程在网 格中 显示 Recordset 对<br>象 rs 的内 容 DisplayKeysetGrid 子例 程使用 三个 参数 Recordset 对 象的名称 MSFlexGrid<br>对象的名 称和一个控制 数据的 显示方向的整数值 因 为键集 游标的功能大 于只向 前游标<br>所以该 子例 程包 含了 可以利 用 这些 功能 的增 强内 容 DisplayKeysetGrid 子例程 的代 码如 下<br>Private Sub DisplayKeysetGrid _<br> (rs As ADODB.Recordset,Grid As MSFlexGrid,nDirection As Integer)<br> Dim fld As ADODB.Field<br> Dim nForward As Integer<br> Dim nReverse As Integer<br> nForward = 1<br> nReverse = 2<br> Set up the grid Grid.Cols = rs.Fields.Count<br> rs.MoveLast<br> Grid.Rows = rs.RecordCount + 1<br> Grid.Row = 0<br> Grid.Col = 0<br> Set up the Grid headings<br> For Each fld In rs.Fields<br> Grid.ColWidth(Grid.Col) = _<br> TextWidth(String(fld.ActualSize + 4 "a"))<br> Grid.ColAlignment(Grid.Col) = 1<br> Grid.Text = fld.Name<br> If Grid.Col < rs.Fields.Count - 1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br>Next fld<br>If nDirection = nForward Then<br> rs.MoveFirst<br> Move through each row in the record set<br> Do Until rs.EOF<br> ` Set the position in the grid<br> Grid.Row = Grid.Row + 1<br> Grid.Col = 0<br> Loop through all fields<br> For Each fld In rs.Fields<br> Grid.Text = fld.Value<br> If Grid.Col < rs.Fields.Count - 1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br> Next fld<br> rs.MoveNext<br> Loop<br> Else<br> rs.MoveLast<br> Move through each row in the record set<br> Do Until rs.BOF<br> Set the position in the grid<br> Grid.Row = Grid.Row + 1<br> Grid.Col = 0 Loop through all fields<br> For Each fld In rs.Fields<br> Grid.Text = fld.Value<br> If Grid.Col <rs.Fields.Count -1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br> Next fld<br> rs.MovePrevious<br> Loop<br> End If<br>End Sub<br>与前面 提到 的 DisplayForwardGrid 子例 程一 样 由子例程 DisplayKeysetGrid 使用的 参<br>数允许 由许 多不 同的 Recordset 和 Grid 对 象 重复使 用 然而 由于这个子例程 主要是用于<br>键集游标 该种游标支 持向前或向后 滚动 所以它使 用了附 加的参数 控 制数 据 列出的 方<br>向 这个 子例 程的 内部 与 DisplayForwardGrid 子 例 程 也有点 儿不 同 由于 Keyset Recordset<br>对象有 不同 的功 能<br>在这个子例程 的开头 声明了两个 整型变量 并且赋了值 这些变量 只用来提高程序<br>后面部分的可读性 这些变量后面的代码建立网格 这段代码非常类似于前面显示的<br>DisplayForwardGrid 然而 这是一个 值 得 注意的差 别 由于键集 游 标 支持向后 移 动 所以<br>这个子 例程 可以 使用 Recordset 对象 的 MoveLast 方法移动到 该 Recordset 的结 尾 这将填充<br>Recordset 对象 然后 可以 根据 相应 的行 数确 定网 格的 大小 在 这 个示 例中 这样 确定 网 格<br>的大小 Recordset 的 Record Count 属性 的值 再加 上 用于列 标题 的一 个附 加行<br>接下来 确定 网格 列的 大 小 列标 题设 置为 在 Recordset 对象 的 Fields 集合中每一 个 Field<br>对象的数据库列名称 这 段 代码与子 例程 ForwardOnlyGrid 相同 这之 后的 下一 节代 码说<br>明如何 使用 键集 游标 的向前 和 向后 的滚 动功 能 传送到 DisplayKeysetGrid 子例程第 三个 参<br>数的值 控制 Recordset 数 据在 网格 中的显 示方 向 值 1 使 数据按照向 前顺 序列 出 值 2 使<br>Recordset 数据 按照 向后 顺序 列出 If 测试比较 nDirection 变 量的值和 整数 变量 nForward 的<br>值 如果 值相 等 那 么执行 If 语句 中第 一部 分的 代码 这 段 代 码与前面 DisplayFordGrid 子<br>例程中 的代 码基 本相 同 MoveNext 方法 和 Do 循环读 取 Recordset 对象 中的 全部行 For Each<br>循环从 Fields 集 合 中的 每 一 行中复制 数据 并放 入网格<br>If 语句的第二 部分 代码 在结 构上是 相 似的 但作 用相 反 Recordset 对象 的 Movelast 方<br>法 将游 标放 在 Recordset 的最 后一 行上 然后 Do 循环 和 Recordset 对象 的 MovePrevious<br>方法从 后向 前读 取 Recordset 再用 For Each 循环 从 Fields 集合的每 个 Field 对象提取 值<br>DisplayKeysetGrid 子例程能 按照 向前 顺序 或者 向后 顺序 在 Recordset 对象中显 示 数 据 而前<br>面的示 例只 是按 照向 前顺序 显 示数 据 下面 的子例 程说 明 如何 使用 DisplayKeysetGrid 子例<br>程和 Keyset 类型的 Recordset 按 照 相反的 顺序显 示 Recordset 数据<br>Private Sub KeysetRecordsetReverse() Dim rs As New ADODB.Recordset<br> Screen.MousePointer = vbHourglass<br> Pass the open method the SQL and Recordset type parameters<br> rs.Open "Select From employee'',_<br> cn,adOpenKeyset,adLockReadOnly,adCmdText<br> ''Display the grid -- use a 2 to display in reverse order<br> DisplayKeysetGrid rs, Grid,2<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>这个示 例说 明了 其与 前面的 那 些示 例的 明显 差别 除 了使用 DisplayKeysetGrid 子例程<br>按照相 反顺 序显 示 Recordset 之外 该子 例 程还 说明如何使 用 Recordset 对象 的 Open 方法<br>的第 一个 和第 二个 参数 传 送源 和 连接 信息 使用 Open 方法的 第一个和第二个参数也可以<br>明确地 对 Recordset 对象 的 ActiveConnection 和 Source 属 性赋值 第一 个参 数设置 Source<br>属性为 简单 的 SQL Server 语句 该语 句将 从 employee 表中检 索全 部行 第二 个 参数设 置<br>ActiveConnection 属 性为 一 个已经 存在 的 Connection 对象 cn 第三个参 数指 定键 集游 标<br>第四个 参数 设置 锁定 类型为 只 读 第五 个参 数识 别第一 个( 源 ) 参数 为 一个命令 文本<br>当 Open 方法 完成 之后 调用 DisplayKeysetGrid 函数 这个 打开 的 Recordset 对象的 名<br>称传送到 第一个参数中 某个已经存 在的网格名称用 在第二 个参数中 在 第三 个 参数中 用<br>值 2 来设 置显 示顺 序为 向 后<br>6.3.4 关闭 Recordset<br>在结束 应用 程序 之前 使用 Recordset 对象的 Close 方 法关闭任何 打开 的 Recordset 对<br>象 下面 是一 个 Close 方 法 的示例<br>rs.Close<br>另外 通过 设置 Recordset 对象 为空 也可 以关 闭连接 如下所 示<br>Set rs = Nothing<br>6.4 使用 Recordset 对象修 改 SQL Server 数据 库<br>可以采 用许 多方 法使 用 ADO 修改数据 首先 ADO 支 持可修改 的 Recordset 对象<br>它可以 使用 AddNew Update 和 Delete 方法 修改 包含 在一 个可 修改 的 Recordset 对象中 的<br>数据 ADO 还支 持使 用动态 的 和预准 备的 SQL 语 句 修改数 据 在本 章的 下一 部 分 将说<br>明如何 使用 Recordset 对象 修改 SQL Server 数据 然 后是几 个示 例 说明 如何 使 用预准 备<br>的 SQL 和 Command 对象修 改数 据<br>除了执 行查 询之 外 Recordset 对 象还 可以用来 修改 数据 然而 当看 到 Recordset 对象的 Open 方法 的各 种参 数 之后 并 不是全 部的 Recordset 对 象 都 是可修改的 修改 Recordset<br>对象的 能力 依赖 于该 Recordset 对象 使用 的游 标类 型 以 及使 用的 锁定类型 这两 种因素既<br>可以使用 Open 方法 的参 数 指定 也可以在打开 Recordset 之前指定为 Recordset 的对 象<br>CursorType 和 LockType 的属性<br>CursorType 和 LockType 两种属性 都影 响修 改 Recordset 对 象的能力 表 6-7 总结了<br>Recordset 对 象的 游标 和锁 定 类型以 及他 们支 持数 据修 改方 法的 能力<br>表 6-7 Recordset 的游标 锁定类型及修改性<br>Recordset 游标类型 可修改吗?<br>adOpenForwardOnly 是( 仅当前行)<br>adOpenStatic 否<br>adOpenKeyset 是<br>adOpenDynamic 是<br>adLockReadOnly 否<br>adLockPessimistic 是<br>adLockOptimistic 是<br>adLockBatchOptimistic 是<br>adLockReadOnly<br>锁定类 型参 数优 先于 游标类 型 参数 例如 如果 锁定类型 设 置为 那<br>么无论 使用 哪一 种游 标类型 结果 集都 是不 可修 改的<br>6.4.1 在Recordset 对 象 中插入 行<br>可以使 用 Recordset 对象 的 AddNew 方法和 Update 方 法在一个 可修 改的 ADO 结果集<br>中增加 行 下面 的代 码说明 如 何在 Recordset 对象 中 增加行 该 Recordset 对象是使用 一 个<br>键集游 标创 建的<br>Private Sub CursorAdd(cn As ADODB.Connection)<br> Dim rs As New ADODB.Recordset<br> Dim i As Integer<br> Screen.MousePointer = vbHourglass<br> pass in the SQL, Connection,Cursor type,lock type and<br> source type<br> rs.Open "Select Dep_ID,Dep_Name From department",_<br> cn, adOpenKeyset, adLockOptimistic, adCmdText<br> Add 50 rows to the department table<br> Note that the Bang ! notation is used to specify column names<br> For i = 1 To 50<br> rs.AddNew<br> rs!Dep_ID = i<br> rs!Dep_Name = "Department" & CStr(i)<br> rs.Update<br> Next Display the new rows in a grid<br> DisplayKeysetGrid rs,Grid,1<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>该 Recordset 对象 的 Open 方法 的第 一个 参数 接受 一个 字符 串 该字 符串 包含 一 条定义<br>该结果 集的 SQL 语句 在 本例中 结果 集由 department 表的 Dep_ID 和 Dep_Name 两个列<br>组成 该表 是在 前面 的动态 SQL 示例 中创 建的 Open 方 法的第 二个 参数 包含 一 个活动 的<br>Connection 对象 cn 的名 称 第三 个参 数使 用常量 adOpenKeyset 指定该 Recordset 对象将 使<br>用一个 键集 游标 第四 个 参数包 含值 adLockOptimistic 这两个参数 表 示该 Recordset 对象<br>集是 可修 改的并且 使用 优化记录 锁 定 当该结 果集 打 开之后 使用一个 For Next 循环在<br>Recordset 对象 中增 加 50 行 在该 For Next 循环内 调用 AddNew 方 法创建一 个 行缓冲 区<br>该缓冲 区 包 含新行的 值 不 像 在 本 章前面的 示 例 中通 过 在 Fields 集 合 中循 环来 访 问列 这<br>个示例 说明 如何 使用 列名和 Bang(!)符号 来访 问一 个示 例<br>提示 这种符号是大小写敏感的 当使用 Bang 符号 时 必须用 完全 与数 据库 显 示 的<br>相同列 名来 指定 列名 否则 在运行 时会 生成 错误<br>通过 使用 循环 计数 器 获 取 的一个 唯一 的 整 数值 来设 置 Dep_ID 列的值 通过 使用 连接<br>文字 Department 和循环 计数 器的 字符 串表 示法 所形 成的 字符 串 来设置 Dep_Name 列<br>当行 值设 置之 后 调用 Update 方法在 Recordset 对象 和数据 源中 增加 行 接下 来 调用<br>DisplayKeysetGrid 子例程 在网 格中 显示 该新 行值 最后 使用 Close 方 法关闭 该 Recordset<br>对象<br>6.4.2 使用 Recordset 对象 修改 行<br>可以使 用 Recordset 对象 的 Update 方法来 修改 在可 修 改的 ADO 结 果集中 的行 下面的<br>代码说 明如 何修 改 Recordset 对象中 的行 该 对象 是 使用一 个键 集游 标创 建的<br>Private Sub CursorUpdate(cn As ADODB.Connection)<br> Dim rs As New ADODB .Recordset<br> Dim i As Integer<br> Dim sTemp As String<br> Screen.MousePointer = vbHourglass<br> Pass in SQL,Connection,cursor type,lock type and source type<br> rs.Open Select Dep_ID,Dep_Name From department ,_<br> cn,adOpenKeyset,adLockOptimistic,adCmdText<br> Do Until rs.EOF<br> `Trim off the blanks because ADO Doesn''t truncate the data<br> sTemp = Trim(rs!Dep_Name)<br> rs!Dep_Name = Updated & sTemp `Update the row<br> rs.Update<br> rs.MoveNext<br> Loop<br> Display the updated rows in a grid<br> DisplayKeysetGrid rs,Grid,1<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>这里同 样使 用 Recordset 对象 的 Open 方 法 创建 一个新的 Recordset 对象 rs Open 方法<br>的第 一个参数 接受 一个 字符串 该字 符串指定 结果 集 在这 个示例中 Recordset 对象由<br>Department 表中的 两个 列 Dep_ID 和 Dep_Name 组成 在第 二个 参数 中 使用 一个活 动的<br>Connection 对象 cn 在第 三个 和第 四个 参数 中 使用 常量 adOpenKeyset 和 adLockOptimistic<br>表示该 Recordset 对象 使用 一个 可修 改的 键集 游标 和 优化记 录锁 定<br>当 Recordset 对象 集创 建之 后 Do Until 循 环 将读取 该 Recordset 对象 中的 全部 行 当<br>Recordset 对象 的 EOF 属性为 真时 循环 结束 在该 Do 循环 内 Dep_Name 列的值 设置 为<br>一个新 的字 符串 值 该字符 串是 以由 文 字 Updated 和 当前的列 值连 接 而成开始的 然后<br>调用 Update 方 法修改 行 Recordset 对象 并且 MoveNext 方 法定位 游标 到下 一行 当 Recordset<br>对象中 的全 部行 修改 完之后 DisplayKeysetGrid 函数显 示已 经修 改的 department 表的内 容<br>最后 使用 Close 方法 关闭 该 Recordset 对象<br>6.4.3 从Recordset 对 象 中删除 行<br>Recordset 对象 的 Delete 方法 删 除在可 修改 的 Recordset 对象中的 行 下面 的代 码说 明<br>如何删 除一 个仅 向前 结果集 中 的行<br>Private Sub CursorDelete(cn As ADODB.Connection)<br> Dim rs As New ADODB.Recordset<br> Dim i As Integer<br> Screen.MousePointer = vbHourglass<br> Pass in the SQL, Connection,cursor type,lock type and source<br> type.Note that this is a forward-only cursor but it can update<br> the current row.<br> rs.Open" Select Dep_ID,Dep_name From department", _<br> cn,adOpenForwardOnly,adLockOptimistic,adCmdText<br> ''Delete all of the rows<br> Do Until rs.EOF<br> rs.Delete rs.MoveNext<br> Loop<br> ''Display the empty Recordset in a grid<br> DisplayForwardGrid rs,Grid<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>就像在 前面 那些 示例 中一样 使用 Open 方法创 建一 个新 的 Recordset 对象 rs 它包含<br>表 department 中的 两个 列 Dep_ID 和 Dep_Name 第二 个参 数包 含一 个活 动的 Connection<br>对象 rs 的名 称 第三 和第四 个参 数包 含常 量 adOpenForwardOnly 和 adLockOptimistic 它<br>们指定 该结 果集 将使 用一个 仅 向前 游标 该游 标支 持 使用优 化记 录锁 定 进行 修 改<br>提示 仅向前 记录集常常被看作是只读的 因为 它们不支持像键集游 标的那种功能<br>然而 仅向 前 Recordset 对象确实支 持修 改当 前行 并且在 ADO 中 它们提 供<br>了 比 键集 或者 动态 的游 标 更好 的性 能 对数据源的 任何变化 都不 反映 到仅 向前<br>Recordset 对象 中 直 到 该对象被刷 新为 止<br>当 Recordset 对象 创建之后 Do Until 循环读取包 含在 Recordset 对象 中的全部行<br>Recordset 对象 rs 的 Delete 方法 删除 每一 行 MoveNext 方 法定位游 标到 结果 集 的下一 行<br>当全部 行都 被删 除之 后 DisplayForwardGrid 子例 程显 示空 的 department 表 最后 Close<br>方法关 闭该 Recordset 对象<br>6.5 使用 Command 对象 操纵 SQL Server 数据 库<br>使用预 准备 的 SQL 语 句 和 参数标 记的 能力 是允 许 ADO 用 于开发 高性 能数 据库 应 用程<br>序的特 征之 一 正像 前面讨 论 ODBC 指出的那 样 在数 据库应用 程 序中使 用 预准 备的语句<br>是可以 大幅 度提 高性 能的一 些 比较 小的 变化 之一 每 一次执 行动 态 SQL 语句 时 甚至 是该<br>语句重 新使 用时 必 须对动 态 SQL 语句 进行 语法 分析并创建数据访问 规划 虽然 动态 SQL<br>语句可 以很 好地 进行 ad hoc 查询 但是 它也 不是执 行重复 SQL 语 句类型的 最好 方法 这种<br>语句构 成在 线事 务处 理(OLTP) 类型 应用 程序<br>6.5.1 执行 静态 SQL 语句<br>预准备 的 SQL 有时 也称为 静态的 SQL 比较 适合 于 OLTP 应 用程序 其中可以反 复<br>使用 SQL 语句 使用 预准备 的 SQL 语句 SQL 的语法 分析 和数 据访 问规 划的 创建 只执 行<br>一次 以后 对预 准备 的语句 的 调用 非常 快 因 为已 经 有了编 译好 的数 据访 问规 划<br>提示 SQL Server 6.5 对于预 准备 SQL 语句 在 tempdb 数 据库中创 建临 时的 存 储过 程<br>而 SQL Server 7 及 2000 在其过 程缓 存 区 中创建数 据 访问规 划 过程 缓存 区是<br>SQL Server 缓存 区的 一部 分 它是 由 SQL Server 使 用的工 作内 存区 虽然存<br>储在过程缓存 区中的数据 访问规 划由所有用户 共享 但是每一个 用户都有一个单独执 行的 内容 另外 为 ad hoc SQL 语句 查询 创 建的访 问规 划也 可以 存储 在<br>SQL Server 的语 句缓 存区 中 不过 如果 执行 该规 划 的成本 超过 了一 定的 内 部<br>线程 它 们才能存储 并且它们只能在“ 安全” 情况 下重新使用 不能 依赖 于为<br>预准备SQL 语句 创建 的数据 访 问规 划<br>下面的 代码 示例 说明 如何创 建 一个 使用 预准 备 SQL 语句的 ADO 查询<br>Private Sub CommandPS()<br> Dim cmd As New ADODB.Command<br> Dim rs As New ADODB.Recordset<br> Screen.MousePointer = vbHourglass<br> With cmd<br> Use a global Connection object -- cn<br> .ActiveConnection = cn<br> Set up the SQL statement<br>. . CommandText = "Select From sales Where stor_id = ? "<br> Add the parameter (optional)<br> .CreateParameter, adchar, adParamInput, 4<br> Set the parameter Value<br> .Parameters(0),Value = "7131"<br> End With<br> Set up the input parameter<br> Set rs = cmd.Execute<br> DisplayForwardGrid rs, Grid<br> rs.Close<br> Screen.MousePointer = vbDefault<br>Ens Sub<br>在这个 子例 程的 开头 创 建了一 个新 的 Command 对象 cmd 和一 个 Recordset 对象 rs<br>该 Command 对 象将 用来 创 建和执 行预 准备 的 SQL 语句 而 Recordset 对 象用来 保 存 返回的<br>结果集<br>接下来 Visual Basic 的 With 语句 块 使用一 组 Command 对象的 属性 在 With 中 第<br>一行代 码设 置该 Command 对象 的 ActiveConnection 属 性为一 个活 动的 ADO Connection 对<br>象 cn 的名 称 然后 CommandText 属性 赋 予 了一个 字符串 该字 符串 包含 将要执 行 的 SQL<br>语句 该 SQL 语句将 返回 sales 表 中的全 部列 其中 stor_id 列 的值等于 在运 行 时提供 的值<br>问号(?) 是一 个参 数标 记 每一 个可 替代 的参 数都 必须 使用 一个 问号 表示 该示例 SQL 语句<br>在 Where 子 句中 使用一 个参 数 所以 只需 要一 个问 号标 记 接下 来 CresteParameter 方法<br>定义该 参数 的属 性该 CreateParameter 语句接受 四个 参 数 第一 个可 选参数 接受 一个 字符 串 它 用 于给该<br>参数命名 第二个参数接受一个 Long 变量 它 识别 该 参数使用 的数 据类 型 在前面的 示<br>例中 adChar 值表 示该 参 数包含 字符 数 据 CreateParameter 语句 的第 三个 参数 指定 该参数<br>是否用 于输 入 输出 或者两 者 都有 值 adParamInput 表示这 是一 个只 用于 输入的 参 数 表<br>6-8 列出了该 参数 允许 的值<br>表 6-8 ADO 参数方向常量<br>ADO 方向常量 描述<br>只输入参数<br>adPramInput<br>输出参数<br>adParamOutput<br>用于输入和输出的参数<br>adParamInputOutput<br>该参数包含了存储过程的返回值 它一般只用作第一个参数<br>adParamReturnValue<br>(Parameter(0))<br>第四个 参数 指定 该参 数的长 度 在前 面的 示例 中 值 4 表 示该参数 有 四 个字节长<br>7131 Parameters Parameter (<br>当参数 的特 征指 定之 后 数值 放进 了在 集合 中第 一个 对象 在<br>本例中 是唯 一的) 的 Va l u e 属性 中 Parameters(0) 对应于 在 SQL Select 语 句中使用 的? 参数标<br>记 把数 值 7131 赋给 Parameter 对象 的 Va l u e 属 性则使 该 SQL 语句作出如下 判 断<br>Select From sales Where stor_id = 7131<br>接下来 Command 对象的 Execute 方法在 SQL Server 上运行该 Select 语句 由于 该 SQL<br>Select 语句 返回 一个 结果 集 所以 cmd 对 象的 输出 赋给了一 个 Recordset 对象 然后 把<br>Recordset 对象 rs 传送 到 DisplayForwardGrid 子例程 中 这将显示 Recordset 对 象 的内容<br>最后 使用 Close 方法 关闭 该 Recordset 对象<br>如果该 cmd 对象 只打 算执行 一次 那 么使用 Recordset 对 象执行该 查询 不 能提高 性 能<br>然而 多次 执行 该 Command 对象可 以提 高性能 因为 SQL 语 句和访 问规 划已 经 准备好 了<br>为了多 次执 行一 个 Command 对象 只需为 Parameter 对象的 Va l u e 属性赋予一个新值 然<br>后重新 运行 该 Command 对象的 Execute 方法<br>6.5.2 执行 动态 SQL 语句<br>ADO 还可 以用 来在 远程 数 据库上 执 行动 态的 SQL 语句 动态 的 SQL 语 句可以 用于 各<br>种数据 管理 和数 据操 纵任务 下面 的示 例说 明如 何在 pubs 数 据库中创 建 department 表<br>Private Sub CreateTable(cn As ADODB.Connection)<br> Dim sSQL As String<br> On Error Resume Next<br> Screen.MousePointer = vbHourglass<br> Make certain that the table is created by dropping the table<br> If the table doesn''t exist the code will move on to the<br> next statement<br> sSQL = Drop Table department cn.Execute sSQL<br> Reset the error handler and create the table<br> If an error is encountered it will be displayed<br> On Error GoTo ErrorHandler<br> sSQL = "Create Table department "_<br> & " (Dep_ID Char(4) Not Null,Dep_Name Char(25), "_<br> & "Primary Key(Dep_ID)) "<br> cn.Execute sSQL<br> Screen.MousePointer = vbDefauit<br> Exit Sub<br>ErrorHandler;<br> DisplayADOError<br> Screen.MousePointer = vbDefault<br>End Sub<br>这个 CreateTable 子例 程实 际上 执行 两个 单独 的 SQL 查询 动作 第一 个语 句删 除 表<br>第二个 语句 重建 该表 SQL Dorp 语句确 保在 运行 SQL Create 语句 之前 该表 不存在<br>在该子 例程 的开 头 Visual Basic 的 On Error 语 句激 活该子例程 的 错 误处理 在这 第 一<br>个实例中 创建错误句柄 来捕捉 任何运行时的错误 然后 用 错 误 之后的 语 句重新 执行 该 子<br>例程 这种 方法 捕捉 当没有 存 在的 表时 执行 SQL Drop 语句可能生成 的 错 误<br>使用 ADO Connection 对象的 Execute 方法是 执行 动态 SQL 语 句最简 单的 方法 在这<br>个示例 中 一个 当前 连接到 SQL Server 的已 有 Connection 对 象执行 该 SQL 语句 Execute<br>方 法 的第 一个 参数 带一 个 字 符串 它包含了将要执行的命令 第一个实例使用 SQL Drop<br>Table 语句 它将 删除 表 department 的任何已 有实 例<br>接下来 如果碰到任何运行时的错误 则 Visual Basic 的 错 误句 柄重 新设 置分 支 为<br>ErrorHandler 标签 这将 允许 在创 建 department 表 时 碰到的 任何 错误 都 由 DisplayADOError<br>子例程 显示 有关 ADO 错 误处理 的详 细内 容 参见 本章 后面 的“错 误处理” 一节 然后 使<br>用 Connection 对象 的 Execute 方法执 行 SQL Create Table 语句<br>注意 department 表不 是 pubs 样本 数据 库的 一部 分 为了 说明 数据 库修 改技 术 没 有<br>修改pubs 数据 库中 原有 表的 内 容 而 创建了 department 表<br>6.5.3 修改 数据<br>上一节 介绍 如何 使用 Recordset 对象 和游 标修 改 SQL Server 数据库 然而 对于编 码<br>来说 使用 Recordset 对象 修改 数据 是比 较简 单的 但是对 于性 能来 说 这 种方法通常不是<br>最优的 使用 预准 备 SQL 语句修改 数据 常常能提 供 更好 的 性能 特别 是对 于 OLTP 类型的<br>应用程 序 其中 SQL 语句有高 度的 重用 性 接下 来 将介绍如何 使用预准 备的 SQL 语句<br>和 Command 对象 的 Execute 方法 在 SQL Server 表中插 入 修改 和删 除数 据<br>使用 Command 对象 和预 准 备 SQL 语句 插入 行 SQL Insert 语句在 表中 增加 行 下面 的<br>示例说 明如 何使 用 Insert 语句和 Command 对象<br>Private Sub PreparedAdd(cn As ADODB.Connection) Dim cmd As New ADODB.Command<br> Dim rs As New ADODB.Recordset<br> Dim i As Integer<br> Screen.MousePoiner = vbHourglass<br> Set up the Command object''s Connection,SQL and parameter types<br> With cmd<br> .ActiveConnection = cn<br> .CommandText = "Insert Into department Values(?,?)"<br> .CreateParameter , adChar,adParamInput, 4<br> .CreateParameter , adChar, adParamInput, 25<br> End With<br> Execute the prepared SQL statment to add 50 rows<br> For i = 1 To 50<br> cmd.Parameters(0) = CStr(i)<br> cmd.Parameters(1) = "Department" & CStr(i)<br> cmd.Execute<br> Next<br> Create a recordset to display the new rows<br> rs.Open"Select From department",cn, , , adCmdText<br> DisplayForwardGrid rs, Grid<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>在这个示例中 创建新的 Command 和 Recordset 对象 然后 Command 对象的<br>ActiveConnection 属 性接 受 一个活 动的 Connection 对象 cn 的名 称 接下 来,CommandText 属<br>性赋予 了一 个 SQL Insert 语句 该语 句使 用两 个参 数标 记 然后 使用 CreateParameter 方<br>法指定 每个 参数的特 征 第 一个参 数包 含一个字 符值 其长 度为 4 个字 节 第 二 个参数 包<br>含一个 长度 为 25 个字 节的 字符 值 正如 所期 望的 Insert 语句那 样 这两 个参 数都 是只 输入<br>的<br>For Next 循环 在表 中增 加 50 行 在 For Next 循环 内 赋予了每 一个参数 使用 的 值<br>cmd.Parameter(0) 对 象引用 第 一 个参数标 记 cmd.Parameter(1) 对象引用第二个 参数标 记 就<br>像在前面那个使用游标增加行 的 示 例 那 样 第一个参 数(Dep_ID 列) 有一 个基 于循 环计 数器<br>的唯 一整 数值 第二个参数(Dep_Name) 有一个字符串 它包 含文 字“department” 和循环计<br>数器 的字符串 表 示 法 当 设置 参 数值之后 使用 Execute 方 法执 行 该预准备 语 句 Display<br>ForwardGrid 子例 程在 网格中显 示 department 表的内容 然后 关闭 该 Recordset 对象<br>使用Command 对象 和预 准 备 SQL 语句 修改 数据 SQL Update 语 句修改 表中 的列 下面<br>的示例 说明 使用 SQL Update 语句 和 Command 对象修改 department 中的全部 行Private Sub PreparedUpdate(cn As ADODB.Connection)<br> Dim cmd As New ADODB.Command<br> Dim rs New ADODB.Recordset<br> Dim i As Integer<br> Screen.MousePointer = vbHourglass<br> Set upthe Command object''s Connection, SQL and parameter types<br> With cmd<br> .ActiveConnection = cn<br> .CommandText = _<br> "Update department Set Dep_Name = ? Where Dep_ID = ? "<br> .CreateParameter , adChar, adParamInput, 25<br> .CreateParameter , adChar, adParamInput, 4<br> End With<br> '' Execute the prepared SQL statement to update 50 rows<br> For i = 0 To 50<br> cmd.Parameters(0).Value = "Updated Department "&CStr(i)<br> cmd.Parameters(1).Value = CStr(i)<br> cmd.Execute<br> Next<br>''Create a recordset to display the updated rows<br>rs.Open"Select From department",cn, , , adCmdText<br>DisplayForwardGrid rs ,Grid<br>rs.Close<br>Screen.MousePointer = vbDefault<br>End Sub<br>就像在 前面 的 Insert 示例那 样 在该子 例程 的 开头 创建 新的 Command 和 Recordset<br>对象 Command 对象的 Active Connection 属 性 有活动 的 Connection 对象 cn 的名称 这里<br>CommandText 属性 有一 条 SQL Update 语句 该 语句使 用两 个参 数标 记 在本例中 第一<br>个参数 引用 Dep_Name 列 第二 个参 数引 用 Dep_ID 列 然后,CreateParameter 方 法 指定每<br>一个参 数的 特征<br>For Next 循环 修改 表 department 中的全 部 50 行数据 在该 For Next 循环 内 赋予了每<br>一个参 数使 用的 值 并且使 用 Command 对象的 Execute 方 法运行 Update 语句 当修 改完<br>成之后 使用 DisplayForwardGrid 子例 程创 建一 个 Recordset 对象 并且 在网 格中 显示 该对<br>象<br>使用 Command 对象 和预 准 备 SQL 语句 删除 数据 就像 Insert 和 Update 操 作一样 可以<br>使用 Command 对象删除远程数据源中的一行或者多行数据 下面的代码清单说明如 何使<br>用预准 备 SQL Delete 语句 和 Command 对象从 SQL Server 数据库 中删 除数 据行<br>Private Sub PreparedDelete(cn As ADODB.Connection)Dim cmd As New ADODB.Command<br>Dim rs As New ADODB.Recordset<br>Dim i As Integer<br>Screen.MousePointer = vbHourglass<br>Set up the Command object''s Connection and SQL command<br>With cmd<br> .ActiveConnection = cn<br> .CommandText = "Delete department"<br>End With<br>Execute the SQL once (that''s all that is needed)<br>cmd.Execute<br>''Create a recordset to display the empty table<br>rs.Open"Select From department",cn, , , adCmdText<br>DisplayForwardGrid rs,Grid<br>rs.Close<br>Screen.MousePointer = vbDefault<br>End Sub<br>由于使 用了 SQL 的 设 置 在某时 功能 这个 示例 比 前面的 插入 和修 改示 例简 单一 些<br>SQL 使 用 一条 语句 可修 改 多行的 功能 允许 使用 一条 SQL Update 语 句修改表 中的 全 部 50 行<br>数据 就像 在前 面那 些示例 一 样 首先 创建 新的 Command 和 Recordset 对象 然后 Command<br>对象的 ActiveConnection 属性 得到 一个 活动 的 Connection 对 象的名称 接下 来 把一条 SQL<br>语句赋 给该 Command 对象的 CommandText 属性 在本例 中 SQL Delete 语 句不使用 任何<br>参数 因为 该语 句没 有包含 任 何 Where 子句 当运 行 Execute 方法 时 将在 department 表<br>中的全 部行 上执 行 Delete 操作<br>注意 当使 用没 有 Where 子句的 SQL 操作 时 一定 要 非常小 心 这种 强大 的技 术 可以<br>无意中 修改 超出 人们 预想的 数 据<br>当这些 修改 完成 之后 使用 DisplayForwardGrid 子 例 程创建 一个 Recordset 对 象 并在网<br>格中显 示 然后 关闭 该 Recordset 对象<br>6.5.4 执行 存储 过程<br>存储过 程提 供了 访问 SQL Server 数据的最 快的 机制 当 创建 存储过程 时 预编 译的 数<br>据访问 规划 就被 增加 到 SQL Server 数据库 中 通过 使用 这个 已经 存在 的数据 访问 规划 该<br>应用程 序就 不必 对任 何到来 的 SQL 语句 进行 语法 分析 再 创 建新的数 据访 问规 划 这样 就<br>可以较 快地 执 行 查询 或者其他 数据 操纵动作 SQL Server 自动 地在 许多 用户之间共 享 存 储<br>过程<br>存储过程所能 实现的数据库安全性 比通 过直接在目标文件上设置许 可要强大得多<br>例如 可以 限制 直接 对 SQL Server 表的全 部访 问 而只 允许访问存储 过 程 当集 中控 制和管理时 存储 过程 可以 提 供对 SQL Server 数据 库访 问的 完全 控制<br>使用 ADO 可以 按照 与调用 预 准备 SQL 语 句 同样 的 方式调 用存 储过 程 Command 对<br>象调用存 储过程 问号 标记表示每一 个存储过程的输 入和输 出参数 下面 的示 例 是一个 简<br>单的存 储过 程 它 接受 一 个输入 参数 并返 回一 个输出参数<br>Create Procedure CountStoreQty<br>(<br> @stor_id Char(4),<br> @qty Int Output<br>)<br>As<br>Select @qty = Select Sum(qty) From sales Whee stor_id = @stor_id<br>GO<br>在这个 示例 中 CountStoreQty 存 储 过程 接 受 一个字符 变量 它包 含 stor_id 作为输 入<br>并且返 回一 个整 数值 它 包含了 sales 表中匹 配提 供 的 stor_id 的 全部行 的 qty 列的总数 在<br>这个示 例中 使用 SQL Select sum() 函数汇总包 含在 qty 列中的 值<br>注意 在存 储过 程中 使用的 变 量名 称不必 与 在源 表中的列名相匹配<br>下面的 代码 示例 说明 如何使 用 Command 对象调 用 CountStoreQty 存储过程<br>Private Sub CallSP()<br>Dim cmd As New ADODB.Command<br>Dim parm0 As New ADODB.Parameter<br>Dim parm1 As New ADODB.Parameter<br>Dim sSQL As String<br>On Error GoTo ErrorHandler<br>Screen.MousePointer = vbHourglass<br>Use the global cn Connection object<br>cmd.ActiveConnection = cn<br>cmd.CommandType = adCmdStoredProc<br>cmd.CommandText =" CountStoreQty"<br>parm0.Direction = adParamInput<br>parm0.Type = adChar<br>parm0.Size = 4<br>cmd.Parameters.Append parm0<br>parm1.Direction = adParamOutput<br>parm1.Type = adInteger<br>parm1.Size = 4<br>cmd.Parameters.Append parm1<br>parm0.Value = "7076"<br>cmd.Execute<br>Label_Mid.Caption = " Total Qty: "Text_Mid.Text = parm1.Value<br>Screen.MousePointer = vbDefault<br>ErrorHandler:<br> DisplayADOError cn<br> Screen.MousePointer = vbDefault<br>End Sub<br>在这个 子例 程的 开头 可 以看到 创建 了一 个 Command 对象 cmd 和两 个 ADO Parameter<br>对象 param0 和 parm1 使用 Parameter 对象可 以替 代在 本章 使 用预准备 SQL 和 Command<br>对象 一节 中介 绍的 使用 CreateParameter 方法 这两种 技术 都可 以用 来指 定参数 标 记的特<br>征 并且 这两 种技 术都 可 以用来 执行 预准 备 SQL 语 句 和存储 过程<br>接下来 给 Command 对象的 ActiveConnection 属 性赋予一个已经存在 的 Connection 对<br>象 cn 的名 称 这样 就把 Command 对象和 一个 目标 数据源关联 了 然后 给该 Command<br>对象的 CommandType 属性赋予值 adCmdStoredProc CommandText 属性 赋予将要执 行 的 存<br>储过程 的名 称 因为 CommandType 属性告诉 ADO 使用 Command 对 象调用 存储 过程 所<br>以没有 必要 建立 一个 SQL 字符串来 包含 一条 ODBC Call 语句<br>该代码的下一 节说明如何初始化 Parameter 对象 对于每一个 Parameter 对象 设置<br>Direction Type 和 Size 属性 然后 使用 Parameters 集合的 Append 方 法增加 Parameter 对<br>象到 Parameters 集合中<br>注意 必须按照由存储过程或者预准备 SQL 语句使用参数的同样顺序 将每一个<br>Parameter 对象 添加 到 Parameters 集合中 换句 话说 在 为第二个 Parameter<br>对象( 表示 第二 个参 数)执行Append 方法 之前 必 须 为第一 个 Parameter 对象(表<br>示第一 个参 数) 执行Append 方法<br>当 Parameter 对象增 加到 Command 对象 的 Parameters 集合之 后 为 第 一个参数 的 Va l u e<br>属性赋 予一 个字 符串 它 包含一 个有 效的 stor_id 值 该值 将传 送到 CountStoreQty 存储过<br>程的第 一个 参数 中 然后 使用 Command 对象的 Execute 方 法调用该 存储 过程 当完 成对<br>该存储 过程 的调 用之 后 输出参 数的 值 可以在 第二 个 Parameter 对象(param1) 的 Va l u e 属性<br>中使用 在前 面的 示例 中 为 该值赋予 了一个 将要 显 示的文 本框<br>6.6 高 级 应 用<br>现在 已经 介绍 了如 何使用 基 本的 Connection Recordset 和 Command 对 象来查 询和<br>修改 SQL Server 数据库 在这 一节 将介 绍如 何使 用一 些更 高级 的 ADO 功能 例如 如何<br>使用批游 标执行修改 如何编码生成 多个结果集的查 询 提 交和 撤 消 事 务 以及使 用二进 制<br>图像数 据<br>6.6.1 批修 改<br>批修改允许将对 Recordset 对象的全部改变立即全部写回数据源 当使用不相关的Record 集时 例如 使用基 于 We b 的应用 程序 批修改是非常有用的 利用批修 改 可以<br>使用正 常的 AddNew Update 和 Delete 方法 修改 Recordset 对象 当完成 Recordset 对象的<br>全部修 改之 后 使用 BatchUpdate 方 法传 送整 个批修改到数 据 库 中 客户 的 Batch 游标库 生<br>成一个 SQL 查询 同 步本地 的 Recordset 对象 和远 程 SQL Server 系统上 的数 据 下面 的示<br>例说明 如何 使用 Recordset 对象 的 BatchUpdate 方法<br>Private Sub BatchUpdate (cn As ADODB.Connection)<br>Dim rs As New ADODB.Recordset<br>Dim i As Integer<br>Screen.MousePointer = vbHourglass<br>Pass in the SQL, Connection, Cursor type,<br>lock type and source type<br>rs.Open"Select Dep_ID, Dep_Name From department", _<br> cn, adOpenKeyset,adLockBatchOptimistic,adCmdText<br>Add 50 rows to the department table<br>For i = 1 To 50<br> rs.AddNew<br> rs!Dep_ID = i<br> rs!Dep_Name =" Add Batch Department " & CStr(i)<br> rs.Update<br>Next<br>rs.UpdateBatch<br>Display the new rows in a grid<br>DisplayKeysetGrid rs,Grid, 1<br>rs.Close<br>Screen.MousePointer = vbDefault<br>ENd Sub<br>这段代 码非 常类 似于 在本章 前 面 使用 Recordset 对 象修改 行 一节 中 介绍的标准 ADO<br>游标修 改示 例 然而 有 两种重 要的 差别 首先 该 Recordset 对象 的锁定 类 型参 数 赋予了<br>常量 adLockBatchOptimistic 这就告诉 ADO 该 Recordset 对 象将使 用一 个批 游标 当打开<br>该 Recordset 对象 之后 使用 AddNew 和 Update 方法 向本 地的 Recordset 增加 50 行数据<br>记住下面 这一点非常重要 标准的键集游 标可以立即宣布新行到 数据源中 而这 种批游 标<br>直到执 行 UpdateBatch 方法 时 才修 改数 据源 然后 所有 修改 的行 都写 到基 表中<br>提示 CancelBatch 方 法 用来取 消由 BatchUpdate 操 作执行 的全 部悬 挂着 的修 改<br>6.6.2 使用 多个 结果 集<br>ADO 处理 多个 结果 集的 能 力允许 在 一个 Recordset 对象 中发 送多 个 SQL Select 语句以及调 用返 回多 个结 果集的 SQL Server 存储 过程 为了 提交 多个 SQL Select 语句 Recordset<br>对象必须使用一个本地的游标而不是一个服务器端的游标 下面的代码说明如何使用<br>Recordset 对象 处理 多个 结果 集<br>Private Sub MultipleRS(cn As ADODB.Connection)<br>Dim rs As New ADODB.RECordset<br>Dim fld As Field<br>Dim sSQL As String<br>Dim i As Ingeger<br>Dim nFldCount As Integer<br>Screen.MousePointer = vbHourglass<br>Set up the three Select statments<br>sSQL = "Select au_lname,au_fname From authors; "<br>sSQL = sSQL & "Select title from titles; "<br>sSQL = sSQL & "Select stor_name from stores"<br>rs.Open sSQL,cn<br>Set up the grid<br>Grid.FixedRows = 0<br>Grid.Cols = 2<br>Grid.ColWidth(0 = _<br> TextWidth(String(rs.Fields(0).DefinedSize + 2, "a"))<br>Grid.ColWidth(1) = _<br> TextWidth(String(rs.Fields(1).DefinedSize + 2, "a"))<br>Grid.Rows = 1<br>Grid.Row = 0<br>i = 1<br>Do<br>Grid.Col = 0<br>Grid.CellBackColor = &HC0C0C0<br>Grid.Text = "Recordset Number: " & i<br>Grid.Col = 1<br>Grid.CellBackColor = &HC0C0C0<br>Grid.Text = " "<br>Do Until rs.EOF<br> Grid.Rows = Grid.Rows + 1<br> Grid.Row = Grid.Row + 1<br> nFldCount = 0<br> `Loop through all fields<br> For Each fld In rs.Fields<br> Grid.Col = nFldCount<br> Grid.Text = fld.Value nFldCount = nFldCount + 1<br> Next fld<br> rs.MoveNext<br>Loop<br>i = i + 1<br>Set rs = rs.NextRecordset<br>Loop Until rs.State = adStateClosed<br>Screen.MousePointer = vbDefault<br>End Sub<br>在这个 多结 果集 的示 例中 可以 看到 sSQL 字符串 包含 了一 条复 合 SQL 语句 该语句<br>实际 上由 三 个 单独 的 SQL Select 语 句组成 它们 之 间 由分 号 分 开 第一个 Select 语句从<br>authors 表中 返回 姓和 名 第二个 Select 语句 返回 titles 表中 的 title 列 第三 个 Select 语句<br>返回一 个由 stores 表中 stro_name 列构 成的 结果 集 执行这 个复 合的 SQL 语句 返回 三 个不<br>同的结 果集<br>这个复 合的 SQL 语句由 Recordset 对象的 Open 方法 来执行 Open 方 法的第 一个 参数<br>包含复 合 SQL 语句 该 Open 方法的 第二个 参数 包含 一个 活动 的 ADO Connection 对象 cn<br>的名称 Open 方 法之 后的下 一段代 码 建立 一个 网格来包含这三个结 果集 的全 部 数据值 在<br>本例中 这些 查询 中的 列 数是已 知的 所以 Grid.Cols 属 性设置 为 2 然而 行 数是未 知的<br>为了容 纳各 种行 的数 量 网格初 始化 时 只包含 一行 然后 当从 Recordset 对 象 中读取每一<br>行时 增加 Grid.Rows 属性 动态 地扩 展网 格<br>Do 循环 处理 多个 结果 集 该 循 环连续 执行 直到 Recordset 对象 rs 的 State 属性等 于<br>adStateColsed 它表示没有更多的结果集可以使用 在这 个 DO 循环 内 建立 当 前网格 行<br>表示每 一个 结果 集的 开始 包含 字“Recordset Number” 的标 题值 是由 结果 集号 和第 一行 的列<br>标题组 合而 成 另外 当 前 单元格 的 CellBackColor 属 性设置为&HC0C0C0 它 以灰色而 不<br>是默认 的网 格颜 色来 显示 Recordset 标题<br>接下来 Do Until 循环 读取 每一 个结 果集 中的 全部 行 通过 对 Grid.Rows 增加 1 网格<br>动态地 扩展 以容 纳每 一行 网格 中的 当前 行使 用 Grid.Row 属 性增加 然后 使用一 个 For Each<br>循环取 出每 一行 的 Field 对象值 并 移 动到网格中 使用 MoveNext 方 法移动到 当前 Recordset<br>对象中 的下 一行 当该 Recordset 对象 rs 的 EOF 属性 变成 真时 Do Until 循环 结束 当前<br>结果集 中的 全部 行读 完之后 NextRecordset 方法 设置 Recordset 对象 rs 到 下一个可用 的结<br>果集<br>6.6.3 使用 事务<br>事务允 许组 合多 个 操 作 作为一 项 工作来 执行 这 有 助于确 保数 据库 的完 整性 例如<br>从储蓄帐 户到支票帐户 的转帐涉及到 多个数据库操作 只有 这些操作全部 成功完 成时 才<br>完成了这 次转帐 一般 从储蓄帐户到支票帐户的转帐 需要两 个单独的但却 是相关 的操作<br>从储蓄帐 户中转出以及转 入到支 票帐户 如果任何一个操作失败 这次转 帐都没 有完成<br>因此 在 本例中这两种 功能是同一个 逻辑事务的一部 分 它 们 被 组 合成一 个事 务 如果 转出操作 成功 而 转 入失 败 那么 整个 事务都被 取消 并 把 数 据库恢 复 到 转出进行 之 前的状 态<br>SQL Server 支持事 务 但是 并非所 有的 数据 库都 支持事 务<br> 取消事 务<br>在 ADO 中的 Connection 对 象中 允许处 理事 务 Connection 对象 的 RollbackTrans 方法<br>可以 用 来 将 数 据库恢复到 该 事务 发 生 之 前 的 状 态 下 面 的示例说 明 如 何 使 用 RollbackTrans<br>方法<br>Private Sub TransRollBack(cn As ADODB.Connection)<br>Dim rs As New ADODB.Recordset<br>Screen.MousePointer = vbHourglass<br>Start a transaction using the existing Connection object<br>cn.BeginTrans<br>Execute SQL to delete all of the rows from the table<br>cn.Execute"Delete department"<br>Now Roll back the transaction - the table is unchanged<br>cn.RollbackTrans<br>Create a recordset to display the unchanged table<br>rs.Open"Select From department",cn, , , adCmdText<br>DisplayForwardGrid rs, Grid<br>rs.Close<br>Screen.MousePointer = vbDefault<br>End Sub<br>在这个 示例 中 执行 Connection 对象 cn 的 BeginTrans 方法 告诉这 个数据 库开 始一<br>个事务 然后 Connection 对象的 Execute 方法 用来 使用 一个 SQL Delete 语句删除 departmetn<br>表中的全部行 然而 不是将这种改变提交到数据库中 而是使用 Connection 对象的<br>RollbackTrans 方法 取消 事 务 把数 据 库 恢复为 department 表的原始 内容 创建 和 显示一 个<br>Recordset 对象 说明 该表 的内 容在执 行 RollbackTrans 方 法之后没 有改 变<br>提示 SQL Server 在一 个事 务日志文 件中维 护数 据库的 修改 该日 志文 件包 含了 对 数<br>据库所 作的 全部 修改 的记录 事务日 志包 含了 每一 个 事务之 前和 之后 的图 像<br> 提交事 务<br>当事务 成功 完成 时 Connection 对象的 CommitTrans 方法把 事务 写到 数据 库中 在下<br>面的示 例中 说 明了 如何使 用 ADO 开始一 个事 务 然后 提交 到 SQL Server 数据库 中<br>Private Sub TransCommit(cn As ADODB.Connection)<br>Dim rs As New ADODB.RecordsetScreen.MousePointer = vbHourglass<br>Start a transaction using the existing Connection object<br>cn.BeginTrans<br>Execute SQL to delete all of the rows from the table<br>cn.Execute "Delete department"<br>Commit the transaction and update the table<br>cn.CommitTrans<br>Create a recordset to display the empty table<br>rs.Open"Select" From department,cn, , , adCmdText<br>DisplayForwardGrid rs, Grid<br>rs.Close<br>Screen.MousePointer = vbDefault<br>End Sub<br>再次执 行 Connection 对象 的 BeginTrans 方法 通知 该数 据库开 始一 个事 务 Execute 方<br>法用来 执行 SQL Delete 语句 然而 这一 次 使用 Connection 对象 的 CommitTrans 方法把<br>这些改 变提 交到 数据 库中 最后 打开 一个 Recordset 对象 说 明在执行 CommitTrans 方法之<br>后该表 的内 容被 删除 了<br>6.6.4 存储 二进 制数 据<br>至今为 止 本 章介 绍的 这 些方法 都是 使用 存储 在 SQL Server 数据 库中的标准文本和 数<br>字数据类 型 然而 许多现代的数据 库应用程序还需 要处理图 形 和 声音 数据 图 形和声 音<br>数据实 际上 没有 什么 不同 这 两种 数据 类型 只是 特殊的二进制数据 格 式 一 般称为 BLOB( 二<br>进制大 对象) 然而 正像 BLOB 名称 所示 这种 类 型的数 据是 非常 大的<br>SQL Server 可以使 用 Image 或者 LongVarBinary 数据类型在 表中 存储 二进 制图 像数 据<br>ADO Field 对象提 供了 GetChunk 和 AppendChunk 方法 它 们 用来访问 存储 在 SQL Server<br>列中的二进 制 数 据 为了 管 理 二 进 制 对 象的大小 GetChunk 和 AppendChunk 方法 是 必要<br>的 标准的文本和数字数据可以在一个操作中设置和检索 而二进制数据可能有好几个<br>MB(megabyte) 这样 访问 这种数 据需 一 些时 间 而 GetChunk 和 AppendChunk 方法正 好<br>可以做 到这 一点 GetChunk 方法从 ADO Field 对象中检 索 二进 制块状数据 而 AppendChunk<br>方法在 Field 对象中 增加 块状数 据 下面 的 子 例程是 一个使 用 GetChunk 方法 在 SQL Server<br>表中检 索和 显示 二进 制数据 的 示例<br>Private Sub BinaryData(cn As ADODB.Connection)<br>Dim rs As New ADODB.Recordset<br>Dim fld As ADODB.Field<br>Screen.MousePointer = vbHourglassrs.Open "Select"pub_id,logo From pub_info,_<br> cn, , , adCmdText<br>Set up the grid<br>Grid.Redraw = False<br>Grid.Cols = rs.Fields.Count<br>Grid.Rows = 1<br>Grid.Row = 0<br>Grid.Col = 0<br>Grid.Clear<br>Size the grid columns bigger than normal<br>Grid.ColWidth(0) = _<br> TextWidth(String(rs.Fields(0),ActualSize + 4, "a"))<br>Grid.ColWidth(1) = _<br> TextWith(String(200, "a"))<br>Grid.RowHeightMin = Grid.RowHeight(0) 3<br>Set up the headings<br>For Each fld In rs.Fields<br> Grid.Text = fld.Name<br> If Grid.Col < rs.Fields.Count - 1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br>Next fld<br>Move through each row in the record set<br>Do Until rs.EOF<br>Set the position in the grid<br>Grid.Rows = Grid.Rows + 1<br>Grid.Row = Grid.Rows - 1<br>Grid.Col = 0<br>Loop through all fields<br>For Each fld In rs.Fields<br>If fld.Type = adLongVarBinary Then<br> Store the image into a temp bitmap file BintoFile"tempbmp.bmp",fld<br> Grid.CellPictureAlignment = flexAlignLeftCenter<br> Load the Temporary bitmap into the grid<br> Set Grid.CellPicture = LoadPicture("tempbmp.bmp")<br> Else<br> Treat the column as regular data<br> Grid.Text = fld.Value<br> End If<br> If Grid.Col < rs.Fields.Count - 1 Then<br> Grid.Col = Grid.Col + 1<br> End If<br> Next fld<br> rsMoveNext<br> Loop<br> Grid.Redraw = True<br> rs.Close<br> Screen.MousePointer = vbDefault<br>End Sub<br>使用 ADO 处理 二进 制数 据 非常像 处理 标准 的 字 符数 据 但是 也有 些不 同 这个样本<br>子例程 开始 先创 建一 个 Recordset 对象 它包 含 pubs 数 据库中 pub_info 表的 pub_id 和 logo<br>列 至今 为止 这似 乎是标 准 的 然而 在 本例中 logo 列 是一个 Image 数据类型 它包<br>含了每 一个 出版 商徽 标的图 形图 像<br>提示 当访问 二进制对象时要小心 它们一般适合于访问本地数据库 对远程数据库<br>的低速WAN 链 接对 于大 型 二进制对象 可能 会带 来性 能 问题<br>当该 Recordset 对象打开之后 初始化用来显示数据的网格 这里 两个网格列的<br>ColWidth 属性用来设置初始的 列 大小 因 为第一个列是一个 标 准 的 字 符 数 据类 型 所以使<br>用 Field 对象的 ActualSize 属性指定 列的 宽度 然而 由于 第二 个列 包含 了图 形数据 所以<br>ActualSize 属 性不能反 映显 示该 数据 所需的长 度 为了确保该列足够大 第二个列的大小<br>设置为 200 个字 符 接下来 使用 网格 的 RowHeightMin 属 性来提 高网 格中 所有 行的 高度<br>在本例 中 行高 度放 大为原 来 的三 倍 当网 格建 立之后 赋 予 网 格的列标 题 使用 For Each<br>循环迭 代 整 个 Fields 集合 用 于二 进 制 数据类型 的 列标 题与用 于标准字 符 列 的列 标 题 没 有<br>什么差 别<br>接下来 Do Until 循环 读取 Recordset 对象 中的 全部 行 对于 每一 行 For Each 循环检<br>索列数据 并且将数据 放进网格中 由于二进制数据 必须按 照与处理文本 数据不 同的方 式处理 所以 在将 数据 移动到 网 格中 之前 检查 每一 个 Field 对象的 Type 属性 如果 Field<br>对象包 含二 进制 数据 Field 对象的 Type 属性 将等 于常 量 adLongVarBinary 使用 BintoFile<br>子例程 转换 数 据 并 且把其放 在网 格的 CellPicture 属性 中 否则 该数 据将作为标 准 的 文<br>本数据 对待 Field 对象的 Va l u e 属性 将赋 给 grid.Text 属性<br>BintoFile 子 例 程把一 个二进制 列 的 内容 转 换 成一 个 字 节数 组 然后把这 个数组写 到一<br>个临时 的位 图文 件中 这 个 位图文件 可以 使用 LoadPicture 函数 赋给 CellPictwre 属性 该<br>BintoFile 子例程的 代码 如下 所示<br>Private Sub BintoFile(sFileName As String, fld As ADODB.Field)<br>Dim bBuffer() As Byte<br>Dim nLenLeft As Long<br>Dim nChunkSize As Long<br>Remove any existing destination file<br>If Len(Dir$(sFileName)) > 0 Then<br> Kill sFileName<br>End If<br>Re-create a new fiel<br>Open sFileName For Binary As # 1<br>Use a 32K initial chuck size<br>nChunkSize = 32768<br>nLenLeft = fld.ActualSize<br>If nLenLeft < nChunkSize Then<br> nChunkSize = nLenLeft<br>End If<br>Retrieve the binary data in chunks<br>Do<br>ReDim bBuffer(nChunkSize - 1)<br>bBuffer = fld.GetChunk(nChunkSize)<br>nLenLeft = nLenLeft - nChunkSize<br>If nLenLeft < nChunkSize Then<br> nChunkSize = nLenLeft<br>End If<br> Loop Until nLenLeft <= 0<br> Write the data to the file<br> Put #1, , bBuffer<br> Close #1<br>End Sub<br>BintoFile 子例程接 受一 个字 符串 变量 它在 第一 个参 数中 包含 一个 临时 数 据文件 名 称<br>在第二 个参 数中 包含 一个 ADO Field 对象 接下 来 这个 子 例 程检查在 第一 个参 数中 命 名的这个 文件 的存 在性 如 果找到 这个 文件 那么 使用 Kill 语句删除 该文 件 然后 使用 Open<br>语句创 建一 个新 的临 时文件 注意 这个 文件 是以 二 进制模 式打 开的<br>当该临 时输 出文 件创 建之后 其大 小设 置为 最大 32K 并检查 ADO Field 对象的 ActualSize<br>属性 如果 在 Field 对象中的 数据 小于 最大 值 那 么 调整最 大值 如果 该数 据大于 32K 那<br>么二进 制数 据按 照 32K 检索 使用 一个 Do 循 环提 取二进 制 的块数据 并且把 它 放进一 个<br>字节数组中 当使 用 GetChunk 方法 把所有的二进制数据都拷贝到这个字节数组中之后<br>使用 Put 语 句 把 二 进 制数据写到打开的 文 件 中 然后 关闭该文件 包含 在临 时文 件中的<br>二进制 数据 可以 由其 他函数 访 问<br>6.7 错 误 处 理<br>使用 ADO 对象 框架 生成 的 运行时 的错 误放 在 ADO Errors 集合 中 当发 生一 个 ADO<br>运行时 的错 误时 调用 Visual Basic 的错 误句 柄 允 许捕捉 和回 应运 行时 的错 误 这种 与 Visual<br>Basic 的紧 密集 成性 易于 处 理 ADO 错误 下面 的 ShowError 子 例 程说明 ADO 的 错 误处理<br>如何与 Visual Basic 的 On Error 函数集 成<br>Private Sub ShowError(cn As ADODB.Connection)<br>Dim rs As New ADODB.Recordset<br>On Error GoTo ErrorHandler<br>Screen.MousePointer = vbHourglass<br>rs.Open"Select From no_such_table",cn<br>rs.Close<br>Screen.MousePointer = vbDefault<br>Exit Sub<br>ErrorHandler:<br>DisplayADOError cn<br>Screen.MousePointer = vbDefault<br>End Sub<br>该 ShowError 函 数试 图打开 一 个不存 在的表 的 Recordset 对象 在该 函数 的开 头 On Error<br>语句激 活 Visual Basic 的错 误句 柄 在本 例中 当碰 到 一个可 捕捉 的错 误时 On Error 语句使<br>该程序 分支 到 ErrorHandler 标签 中<br>对一个 不存 在的 表执 行 Open 方法 将使 ADO 对象框 架 生成一 个运 行时 的错 误 该错误使该<br>程序 重新 开始 执行 该标 签 后的第 一条语句 在这个示例中 DisplayADOError 子例程将在<br>无效的 Open 方法 之后 执行<br>下面的 程序 清单 说明 DisplayDAOError 子例 程 如 何使用 DAO 的 Error 对象和 Errors 集<br>合在一 个简 单的 消息 框中显 示 一个 有关 DAO 错误情 况的 信息<br>Private Sub DisplayADOError(cn As ADODB.Connection) Dim er As ADODB.Error<br> For Each er In cn.Errors<br> MsgBox "Number: " & er.Number & vbCrLf & _<br> "Source: " & er.Source & vbCrLf & _<br> "Text: " & er.Description<br> Next<br>End Sub<br>在这个 子例 程中 ADO Connection 对 象 作为 一个参数传 送 在 RDO 和 DAO 对象 框架<br>中 Errors 集合 由基 本的引 擎 对象 维护 而 ADO Errors 集合 包含 在 Connection 对象中 接<br>下来 声明 一个 新的 ADO Error 对象 er For Each 循环 在 ADO Errors 集合 中迭代 该循环<br>是必不 可少 的 因为 ADODB.Errors 集合可 以包 含多 个 Error 对象 每一 个对 象都 表示 一个<br>不同的 错误 情形 使用 For Each 循环 Number Source 和 Description 属 性的值 显示 在一<br>个消息 框中 ADO Error 对象的 Number 属性包 含 ADO 错 误消息数 字 Source 属性识别产<br>生该错误的源对象 正如所希望的那样 Description 属 性包含 了该 错误 情况 的文 本描 述<br>子例程 DisplayADOError 显示的 消息 框如 图 6.6 所示<br>图6.6 错误处理消息框<br>6.8 小 结<br>ADO 对象 模型 主要 是用 于 OLE DB 而 OLE DB 企图 对许多 不同 的数 据源 提供 多机 种<br>环境的 数据 访问 除 了访问 像 SQL Server 这样 的关 系型 数据 库之 外 OLE DB 还提供 了 对<br>各种其 他数 据源 的访 问 包括 Excel 电子表 格 Active Directory 和 Exchange 所以 基于<br>OLE DB 的 ADO 是 Microsoft 的主 流数 据访 问技 术第7 章 使用SQL-DMO 管理SQL Server<br>在本章 中 我们 将学 习如何 利 用 SQL-DMO 分布 式 管理对 象 从 Visual Basic 或者其<br>他 OLE 兼容 的开 发语 言中 编程 管理 SQL Server 首先 概述 SQL-DMO 并看看其 基 本 的<br>体系 结 构 然后介绍如 何从 Visual Basic 中使用 SQL-DMO 在这 一 节 中 将介 绍如 何 把<br>SQL-DMO 类型 库增 加 到 Visual Basic 的集成 开发 环境 中 还 将讨论如何 使 用 SQL-DMO 执<br>行一些 常见 任务 最后 通过提 供 两 个使用 Visual Basic 和 SQL-DMO 创建 的 示例实用程 序<br>结束本 章<br>7. 1 概 述<br>SQL Server 拥有管 理自 身的 强大能 力 图形 化管 理工具 MMC 使得 SQL Server 管理员<br>能够快速轻松地控制服务 器 的各个方面 这些 工具 的 底 层基 础就 是 SQL-DMO( 分布 式 管理<br>对象) SQL-DMO 并 不 是一种 新 的 数据库访问技术 它 不关心 数据 库存 储的 信息 主要 负<br>责数据 库本 身的 结构 和维护 以及 对包 含数 据库 的 SQL Server 操作 例如 程序 员经 常使<br>用 SQL-DMO 自动 化一 个接 口以 完成 数据 库管 理任 务 比如 备份 数据 库复 制及性 能 报告<br>SQL-DMO 允 许轻 松地开发适应用户 环境 的 定制 SQL Server 管理 应用 程序 使用 SQL-<br>DMO 可以 使用 Visual Basic 或 者 任何其 他 OLE 兼容 语言来 创建 定制 的 SQL Server 管理界<br>面 该界 面允 许利 用 SQL Server 企业管理 器提 供的功 能 SQL-DMO 允 许应用 程序执 行那<br>些通过 SQL Server 企业管理 器手 动实 现的 功能集 事实上 SQL-DMO 是 SQL Server 企<br>业管理器 的基础 使用 SQL-DMO 可以列出 数据库 和表 增加登录帐户 控制 复制 引<br>入数据 引出 数据 以及 执 行许多 其他 管理 任 务 SQL-DMO 使 SQL Server 面向许多定 制 程<br>序 这些 程序 可以 显示 和 操纵 SQL Server 本身<br>7.1.1 初识 DMO<br>DMO 是一 种 32 位 OLE COM 组件 对象 模型 对象 允许 应用 程序 访问 SQL Server<br>的管理 功能 SQL Server 的 DMO 是在 SQL Server 6 中首 先引 入的 并且 在 SQL Server 7<br>和 2000 中继 续得 到增 强 SQL Server 的 DMO 有 60 多 种不同的 对象 和 1000 多 种 不同的属<br>性和方 法 提供 了可 以访问 SQL Server 深层 功能 的方 法 SQL-DMO 使用 SQL Server ODBC<br>3.x 驱动 程序 连接 到 SQL Server 正如 其名 称所 示 SQL Server 的 DMO 企图促进在分布 式<br>环境中 使用 SQL Server SQL DMO 通过 将 SQL Server 的 管理 功 能扩展 到 网络中 的所 有客<br>户机 来做 到这 一点 可 以使用 SQL-DMO 访问 数据 但是 这不是 其真 正的 目的 其他 一<br>些应用 程序 编程 界面(API) 例如 Active Data Objects(ADO) Data Access Object(DAO) Open<br>Database Connectivity(ODBC) 和 Remote Data Objects(RDO) 是比较好的数据访问机制<br>SQL-DMO 的真 正长 处 是 编程管 理联 网的 SQL Server 系统7.1.2 DMF<br>SQL Server 2000 是通 过名 为 SQL 分布 式管 理框 架(SQL-DMF) 的集成对 象和 组件集 合<br>来管理 的 SQL-DMF 允许使 用 Microsoft Management Console(MMC) 和 SQL Server 企业管<br>理器 或者 通过 使用 SQL-NS SQL-DMO 和 DTS 编程 来交 互管 理 SQL Server 图 7-1<br>示意了 SQL-DMF 的主要组件<br>在 SQL-DMF 体系结构的顶 层 可以 看到 用来 管理 SQL Server 2000 的 不同的终 端用 户<br>应用程 序 SQL Server 企业管理 器是 作为 SQL Server 2000 的 一部分 提供 的 由 SQL Server<br>企业管理器使用的 同样的基本组件也可以用于定制 管理工具 SQL Namespace(SQL-NS) 把<br>SQL Server 企业管 理器 的用 户界 面元 素呈 现给 应用 程序 SQL 分布 式管 理对 象(SQL-DMO)<br>显示的 OLE 界面 可以 用于 控制 SQL Server Engine 和 SQL Server Agent 的各 个方 面 数<br>据转换 服务(DTS) 能转 换和 传输 包含 在 SQL Server 数据库 中的数据 这些 数 据可以用 于数<br>据市场 和数 据仓 库应 用程序<br>图7-1 SQL 的分布式管理框架(SQL-DMF)<br>7.1.3 SQL-DMO 的文 件<br>表 7-1 总结 了用 来在 SQL Server 上实现 SQL-DMO 的 客户机 文件表7- 1 SQL-DMO 的文件<br>目录 文件 描述<br>实现 SQL-DMO 对象的 DLL<br>C:\Program Files\Microsoft SQL Server\ Sqldmo.dll<br>80\Tools\Binn<br>SQL-DMO 帮助文件<br>C:\Program Files\Microsoft SQL Server Sqldmo80.hlp<br>\80\Tools\Binn<br>本地化资源文件 资源目录根据 SQL Server 服<br>C:\Program Files\Microsoft SQL Server\ Sqldmo.rll<br>务器或者客户机的当地语言确定 比如 1033<br>80\Tools\Binn\<br>的语言标识用十进制表示为 0X0409 这个数字<br>Resources\xxxx<br>代表美国英语<br>包含 SQL-DMO 的成员函数 类型 枚举数据类<br>C:\Program Files\Microsoft SQL Server\80\ Sqldmo.h<br>型和宏的 C/C++ 头文件<br>Tools\<br>Devtools\Include<br>包含接口和类标识的 C/C++ 头文件<br>C:\Program Files\Microsoft SQL Server\ Sqldmoid.h<br>80\Tools\<br>Devtools\Include<br>Transact-SQL 脚本实现支持 SQL-DMO 的存储过<br>\Program Files\Microsoft SQL Server\ Sqldmo.sql<br>程 只在 SQL Server 服务器实例中存在<br>MSSQL\Install<br>7.2 SQL-DMO 的核 心 对象分 层结 构<br>SQL-DMO 核 心对 象是按照 SQL Server 企 业管 理器 同一 基本 组织 的分层顺序进行组织<br>的 核心 SQL-DMO 对象 的层 次如 图 7-2 所示<br>图7-2 SQL-DMO 的核心对象层次<br>SQL Server 的分布 式管 理对 象使用 了一 种层 次体 系结构 在 SQL Server 的 DMO 对象<br>框架的 顶部 是应 用程 序 应用程 序下 面是 SQLServer 对象 SQLServer 对象 表示一 种 物理 SQL<br>Server 系统 在 SQLServer 对象下面 的各 种对 象集 合允许 使 用 SQL Server 系 统 的各个 方面<br>例如 RemoteServers 对象 集合 允许创建和管 理远 程服务器 同样 JobServer 对 象 集合 允<br>许控制 SQL Server 的任务 作业 和警 报 在 Databases 集 合中的每 一个 Database 对象表 示<br>一个物 理的 SQL Server 数据库 每一 个数 据库 对象 包含 一个 或者 多个 表对象 以及 其他 类<br>型的 SQL Server 对象 例如 触发 器 视 图 和存储 过程 类似 地 Table 对 象层包 括了 列<br>触发器 及键 和索 引对 象 最后 Replication 对 象允 许建立 和控 制 SQL Server 的 数 据库复制<br>下 一 节详 细研 究这 些领 域 的 每项内容<br>7.2.1 SQLServer 对象 层<br>SQL Server 对象是 最主 要的 SQL-DMO 对象 使用 SQLServer 对 象的实 例可 以访 问所 有<br>其他的 SQL-DMO 对象<br> SQLServers SQLServer 对象是 SQLServer 对象的集合 其中每一个对象都表 示<br>一个物 理的 SQL Server 系统<br> BackupDevices BackupDevices 对象 是 BackupDevice 对 象的集 合 其中 每一 个 对<br>象都表 示一 个包 含数 据库或 者 事务 日志 的文 件<br> Configurations Configurations 对象 包含 了 ConfigValue 集合 每一 个 ConfigValue<br>对像包 含了 一个 控制 SQL Server 的配置值<br> FullTextService FullTextService 对象把全 文本 索引 服务呈现给了 SQL-DMO 应用<br>程序<br> IntegratedSecurity IntegratedSecurity 包含了有关 映射 SQL Server 登录帐 户及 NT<br>用户和 组的 信息<br> Logins Logins 对象是 Login 对 象 的集 合 其中 每 一个 对 象 都 包含一个数 据库的<br>登录帐 号 ID 和口 令<br> Lanuages Lanuages 对象是 Language 对象 的集 合 其 中每一 个对 象都 代表 一种 由<br>本地 SQL Server 系统支 持的 语言<br> Registry Registry 对象包含 了存 储在 本地 系统 的注 册表 中所 有 SQL Server 信息<br> RemoteServers RemoteServers 对象是远 程 SQL Server 系统的集 合 其中 每一 个<br>RemoteServer 对象代表 一个 与本 地 SQL Server 系统连接 的 远程 SQL Server 系统<br> ServerRoles ServerRoles 对象是 ServerRole 对 象 的集合 其中 每一 个对 象包 含了<br>全部 SQL Server 系统角 色<br>7.3.2 Database 对象 层<br>SQL-DMO 的 Database 对 象允许 使用 各种 SQL Server 的数据库 对象 例如 缺省 规则<br>表和存 储过 程 图 7-3 示意 了 SQL-DMO 的 Database 对象 层<br>Database 对象 层位 于主 要的 SQLServer 对象之 后 SQL-DMO 的 主要对 象是 Database 对象<br>下面的 SQL-DMO 对象 包含 在 SQL-DMO 的 Database 对 象层中<br> Databases Databases 对象是 Database 对象的 集合 其中每 一个 对象 都包 含了 与 单<br>个 SQL Server 数据库相 关的 全部 信息<br> <br> 图7-3 SQL-DMO 的 Database 对象层次结构 图 7-4 SQL-DMO 的 Table 对象层次结构<br> DatabaseRoles DatabaseRoles 集合由 DatabaseRole 对 象组成 其中每一 个对 象 都<br>代表一 组有 类似 的安 全性属 性的 用 户<br> DBOption DBOption 对 象 包含了 一个 SQL Server 数据库选 项的 属性 数据 库选<br>项包括 了像 允许 Null 和 DMO 拥有 者 使用这 样的 属性<br> Defaults Defaults 对象 是 Default 对象的 集合 其中 每一 个对 象都 包含 了一 个缺 省<br>值 当没 有为 某个 列或 者 数据类 型提 供数 值时 使 用 该缺省 值<br>FileGroups FileGroups FileGroup FileGroup<br> 对象 是 对象的 集合 每一个 对象 包含<br>了一个 数据 库文 件组 的信息<br> FullTextCatalogs FullTextCatalogs 对 象显示了一个 Microsoft Search 系 统 表的 属<br>性<br> Rules Rules 对象 是 Rule 对象的 集合 其 中每 一个 对 象指定 可以 存储 在数 据库 列<br>或者数 据类 型中 的有 效数据<br> StoredProcedures StoredProcedures 对象是 StoredProcedure 对 象的集合 其 中每一<br>个对 象要 么是 一组 Transact-SQL 语句 要 么是由 一个 动态 链接 库(DLL) 包含的扩<br>展存储 过程<br> SystemDatatypes SystemDatatypes 对象是 SystemDatatype 对象的集 合 其中 每一<br>个对象 代表 一个 由系 统提供 的 数据 类型<br> TransactionLog TransactionLog 对象代 表一 个数 据库的 事务 日志<br> UserDefinedDatatypes UserDefinedDatatypes 对象是 UserDefinedDatatype 对象的<br>集合 其 中每 一个 对象 代 表一个 用户 定义 的数 据类 型<br> Users Users 对象 是 User 对象 的集 合 其中 每一 个对 象代 表一 个单 独 的数 据库 帐<br>户<br> Views Views 对象 是 View 对象的 集合 其中 每一 个对 象代 表一 个包 括来 自一 个<br>表或者 多个 表的 记录 的 Select 语句<br> Tables Tables 对象 是 Table 对象 的集 合 其中 每一 个 对象包 含了 与单 个表 相关 的<br>信息 该表 包含 在一 个 SQL Server 的数据 库中<br>database 对象 的主 要对 象是 表对 象 其结 构具 体如 图 7-4 所示<br> Checks Checks 对象 是 Check 对象的集 合 其 中每 一 个对象 代表 一个 表的 检查 约<br>束<br> ClusteredIndex ClusteredIndex 对象代 表一 个与 单 给 定表相关 的聚 簇索 引<br> Columns Columns 对象 是 Column 对象 的集 合 其 中 每一个 对象 都代 表表 中的 一<br>个列<br> Indexes Indexes 对象 是 Column 对象 的集合 其中 每 一个对 象都 代表 表中 的一 个<br>列<br> Indexes Indexes 对象 包含了 Index 对象 的集 合 其中 每一 个对 象代 表表 中的 一 个<br>索引<br> Keys Keys 对象是 Key 对象的集合 其中 每一 个对 象代 表表 的一 个外 键 主键 或<br>者唯一 键<br> Permissions Permissions 对象是 Permission 对象的集合 其中 每一 个对 象代 表该<br>表的访 问权 限<br> PrimaryKey PrimaryKey 对象代表 一个 与指 定表 相关的 主 键<br> Triggers Triggers 对象 是 Trigger 对象的 集合 其 中 每一个 对象 代表 一组 Transact-<br>SQL 语句 当修 改表 时执行 这 些语 句<br>7.2.3 JobServer 对象 层<br>SQL-DMO 的 JobServer 对象 允许 控制 SQL Server 的 Agent 功能 例如 任务 作 业和警<br>报 图 7-5 示意 了 SQL-DMO 的 JobServer 对象 层<br>图7-5 SQL-DMO 的 JobServer 对象层<br>SQL-DMO Agent 对象 的主 要对 象是 JobServer 对象 该 JobServer 对象 控制 SQL Server<br>的任务 和调 度功 能<br> JobServer JobServer 对象是 JobServer 对象 的集 合 其中每 一个 对象 代表 一个 SQL<br>Server 的调度 引擎<br> Alerts Alerts 对象 是 Alert 对象 的集 合 其中 每一 个 对象代 表一 个 SQL Server 的<br>警报 警报 就是 要求 通知操 作 员的 事件<br> AlertSystem AlertSystem 对象包 含了 有关 警报引 擎的信 息<br> JobCategories JobCategories 集合 包含 Category 对象 包含 有 SQL Server Agent<br>的作业 组织 方法<br> JobFilter JobFilter 对象包含 了限 制将 要监 测的 作业 类型的信 息<br> JobHistoryFilter JobHistoryFilter 对象指定 将要 消除 的作 业历 史记 录<br> Jobs Jobs 对象 是 Job 对象 的集 合 其 中每 一个 对象包 含 了一个 调度 的 SQL Server<br>作业和 与其 相关 的作 业步骤 的 信息<br> OperatorCategories OperatorCategories 集合包 含 Category 对象 指定 SQL Server<br>Agent 操作 员的 分类 方法<br> Operators Operators 对象 是 Operator 对象的 集合 其中 每一 个对 象代 表一 个 SQL<br>Server 操作员 操作 员通 过电 子邮 件或 者页 面警 报来 接收 SQL 事 件的通 知<br> TargetServerGroups TargetServerGroups 对象是 TargetServerGroup 对 象的集合<br>其中每 一个 对象 包含 了有关 SQL Server 系统 组的 信息<br> TargetServers TargetServers 对象 是 TargetServer 对 象 的集合 其中 每一 个对 象包<br>含了作 为远 程作 业目 标的 SQL Server 系统的信 息<br>7.2.4 Replication 对象 层<br>顾名思 义 SQL-DMO 的 Replication 对象 允许 控制<br>SQL Server 的数据 复制 功能 图 7-6 示意 了 SQL-DMO<br>的 Replication 对象 层<br>SQL-DMO 的 Replication 对象允许 为 SQL Server 的<br>合并复 制和 事务 复制 建立和 控 制出 版 分布 和订 阅<br> Replication Replication 对象集合控制 SQL<br>Server 的数据 复制 的出 版 分布 和订 阅角 色<br>每一个 Replication 对象包含 Publisher<br>图7-6 SQL-DMO 的 Replication 对象层<br>Subscriber 和 Distributor 对 象集合<br> Distributor Distributor 对象 包含 有关 分布 服务 器的 信息<br> Publisher Publisher 对 象 包含有 关出 版服 务器 的信 息<br> ReplicationDatabases ReplicationDatabases 对象是 ReplicationDatabase 对象的集<br>合 其中 每一 个对 象包 含 了有关 复制 的数 据库 的信 息<br> Subscriber Subscriber 对象 包含 有关 订阅 服务 器的 信息<br>演示 SQL-DMO 的示例程序<br>C:\Program Files\Microsoft SQL Server\80\Tools\ ALL<br>Devtools\Samples\Sqldmo7.3 SQL-DMO 应用 初 步<br>为了获得其他 数据库平台的管理功能的编 程访问 可能需要掌握低级 的网络和系统界<br>面—— 如果 系统 界面 是可用 的 SQL-DMO 提 供 了一个 OLE 基础 使 SQL Server 的数据 库<br>管理功 能易 于访 问 因为 SQL-DMO 是作为 一组 OLE 对 象实现 的 所以 这些 对 象只能 在 32<br>位客户 应用 程序 中使 用 SQL Server 的 SQL-DMO 功 能可以由 任何 32 位 OLE 兼容的 开<br>Visual Basic Visual C++ Delphi<br>发工具 使用 包括 和 许多其他 工具<br>为了在 Visual Basic 中使 用 SQL-DMO 需要 执行 下列 基本 步骤<br>1) 包括 SQL-DMO 的 类型库(SQL DMO)<br>2) 创建 一个 SQL Server 对象<br>3) 将该 SQL Server 对象连接到 SQL Server<br>4) SQL-DMO<br>使用 的服 务 器对象<br>5) 与 SQL Server 断开连 接<br>7.3.1 将SQL-DMO 对象 添加到Visual Basic 中<br>开始在 Visual Basic 的开 发环 境中 使用 SQL-DMO 对 象之前 需要 安装 这些 对象 当<br>第一次 安装 SQL Server 的 32 位客 户程 序时 把 那些提 供对 DMO 的 基 本支持的 文 件 拷贝到<br>Visual Basic Visual<br>客户机 系统 中 然而 仍 然需要 在 的开 发环境 中进 行设 置参 考 以允许在<br>Basic 中使 用它 们 为了 在 Visual Basic 4 中增加 SQL-DMO 支持 需要从 Visual Basic 的 Tools<br>菜单中 选择 References 选项 以便在 Visual Basic 中参 考 SQL-DMO Type Library 为了在<br>Visual Basic 5 或者 6 中增加 SQL-DMO 参考 必须 从 Project 菜 单中选 择 References 选项<br>该操作 显示 References 对话框 如图 7-7 所示<br>图7-7 设置对 SQL-DMO Type Library 的参考在 References 对话 框上 滚动 直到 看到 选项 Microsoft SQLDMO Object Library 为止<br>单击该 选项 旁边 的复 选框 将 SQL-DMO OLE Object Library 文件添加到 Visual Basic 的交<br>互式开 发环 境(IDE) 中 当在 Visual Basic 的 IDE 中增加 参 考时 不会 在 Visual Basic Toolbox<br>中看到 添 加 的对象 而增 加 ActiveX 控件时 则 可 看到增 加的对 象 为了看 到 SQL-DMO<br>的属性 和方 法 必须 使用 Visual Basic 的 对象浏览 器 如图 7-8 所示<br>图7-8 在对象浏览器中查看 SQL-DMO 库<br>7.3.2 创建 SQL Server 对象<br>在 可 以使 用任 何 SQL-DMO 方法之 前 必须创建 一个 SQLServer 对 象的实例 这是<br>SQL-DMO 集中 最基 本 的 对象并 且必 须在 所有 的 SQL-DMO 应 用程序中 提供 使用 Visual<br>Basic 可以 用三 种不 同的方 法创 建一 个 SQL Server 对象的 实例 使用 New 关键字 使用<br>CreateObject 函 数或 者使 用 一般的 对象 类型<br>创建新 的 SQLServer 对象的 首选方 法是 使用 New 关键 字 如下 所示<br> Dim oSQLServer As New SQLDMO.SQLServer<br>这种方 法创 建了 一个 名为 oSQLServer 的新 SQLServer 对象 它还 创建 了一 个 SQLServer<br>对象的 实例<br>注意 OLE 对 象的 一种 通 用命名 约定 是 在 名称前加 前 缀小写 字母 o 它 表 示 该变量 是一<br>个对象<br>另外 还可 以用 CreateObjects 函数 创建 一个 SQLServer 对象的示 例 如下 所示<br> Dim oSQLServer as SQLDMO.Server<br> Set oSQLServer = CreateObject( SQLDMO.SQLServer )<br>该方法 创建 了一 个名 为 oSQLServer 的 SQLServer 变量 然后 使用 CreateObject 函数生<br>成该对 象 通常 只在 不支持 New 关键 字的 老 Visual Basic 版 本 中使用这种 方法<br>另外 还可 以使 用 Visual Basic 的一 般对 象类 型创 建 一个新 的 SQLServer 对象 如下所 示<br> Dim oSQLServer as Object<br> Set oSQLServer = CreateObject( SQLDMO.SQLServer )<br>所有的Visual Basic 版本 都支 持这 种方 法 然而 这种方法没 有 使 用 CreateObject 函数<br>清楚 另外 这种 一般 的 对象类 型是 这三 种方 法中 最 慢的方 法 因为 它依 赖于 绑 定使用 OLEIDispatch<br>提示 当创 建 SQL Server 对象 使其 作为 一个SQLServer 对 象的实例 时 必须运行Connect<br>方法创 建一 个与 活动 的 SQL Server 系统 链接 后 才能 使用这 种对 象<br>7.3.3 连接 到 SQL Server<br>在 SQLServer 对象创建 之后 可以 使用 该对 象的 Connect 方法 把 SQL Server 对象连 接<br>到 SQL Server 系统上 下面 的代 码示 例说 明如 何使 用 SQL Server 对象 的 Connect 方法连 接<br>到 SQL Server<br> oSQLServer.Connect sSQLServer$, sUserID$, sPassword$<br>这个 Connect 方法 带有 三 个字符 串参 数 第一 个字 符串 参数 包含 SQL Server 的名称<br>第二个 参数 包含 一个 有效的 SQL Server 登录 帐户 的 ID 第三个 参数 包含 了该 帐 户的口 令<br>在 Connect 方法 成功 地完成 连 接之 后 该 SQL Server 对象(在 本 例中为 oSQLServer) 的<br>属性将 用第 一个 参数 所指定的 SQL Server 系统 中相 应的 值填 充 还可 以使用 SQLServer 对<br>象的所 有方 法 对象 和对象 集 合<br>提示 在 Connection 方法 成功 地执 行之 后 可以 使 用所有 的 SQL Server 的对象和集<br>合 直 到第一次 访问每一个 对象 时 才可以 检索到实际 的 SQLServer 值 当第<br>一次访问对象之后 SQLServer 对象把它们存储在本地的高速缓存中 使用<br>Refresh 方法 可使 SQLServer 对 象 修改存 储在 本 地高速缓存中的全 部 对 象<br>7.3.4 获取 属性 值<br>SQLServer 对象有 1000 多个 不同 的属 性 可以 从应 用程 序中 进行 访问 虽然 可以 阅读<br>到所有 的 SQLServer 属性 但是只 能写 一小 部分 的属性 SQL Server 的分布 式管理 对 象帮<br>助(sqldmo.hlp) 在线帮 助文件 列 出了 SQL-DMO 对 象的所有属性 并 且标明它们是只 读 的 还<br>是读写 的<br>提示 可以 使用Visual Basic 的 Object Browser 从 Visual Basic 的 IDE 中 列 出每一<br>个SQLServer 对象 的属 性<br>使用Visual Basic 的赋 值运 算符(=) 可 以检索 出是 标准 数据 类型 的全 部属 性的 属性 值<br>如下所 示<br> Dom sHostName As String<br> sHostName = oSQLServer.HostName<br>在上 面的示 例中 可以 看到 一 个字符 串 sHostName 先用 Visual Basic Dim 语句声 明<br>然后使 用 Visual Basic 的赋 值运 算符 将 oSQLServer 的内容填充到 sHostName 字符串 变量中<br>这种技 术可 用于 标准 的 Visual Basic 数据 类型 例如 String Long 和 Integer 然而 对象<br>属性则 有一 点儿 不同 如 下所示<br> Dim oExecutive As SQLDMO.Executive<br> Set oExecutive = oSQLServer.Executive<br>为了提 取 SQL-DMO 对象 属性 的内 容 必须 使用 Visual Basic 的 Set 语句 Set 语句可用来<br>把一 个对 象 参 考赋 值给一 个变量 在本例中 使用 Dim 语 句声 明一个名 为 oExecutive 的<br>SQLDMO.Executive 数据类 型的 对象 然后 使用 Set 语句 把 oSQLServer.Executive 对象的<br>内容赋 给 oExecutive 对象提示 当对象 超出范围时 与对象相关的 系统资源正常释放 然而 如果在全局函数<br>中 设 置对 象参 考 那么 必 须明确设置该对象为空 以便释放由该对象使用的系<br>统内存 和资 源<br>7.3.5 设置 属性 值<br>使用赋 值运 算符(=) 可以从 Visual Basic 中设 置 SQL-DMO 读 写属性 值 下 面的示 例<br>说明如 何设 置 SQLServer 对象的 ApplicationName 属性<br> Dim sApplicationName as String<br> sApplicationName = MyApp<br> oSQLServer.ApplicationName = sApplicationName<br>在上面 这个 示例 中 可 以 看到使 用 Visual Basic 的赋 值运算 符 将 oSQLServer.Applica<br>tionName 的属性 设置 为 MyApp 该属 性值 包含 在 sApplicationName 字符串 变量 中<br>提示 不能 设置 SQL-DMO 对象 的任 何属 性 这 些对 象 属性是 只读 的 只 能设 置使用标<br>准数据 类型 的属 性 例如 String Boolean 或者 Long<br>7.3.6 SQL-DMO 的属 性集 合<br>在前面的图 7-2 中 SQL-DMO 的 核心对象层扩展了 对象集 合的 使 用范围 它是 有关<br>对象的 基本 组 例如 在 SQLServer 对象中 的 Databases 集合 是 Database 对 象的集合<br>提示 集合 对象 一般 用“s”结束 例如 Databases 表示 Database 对 象的集 合<br>表 7-2 列出 了 SQLServer 对象的部 分对 象集 合<br>表 7-2 SQLServer 的对象集合<br>对象集合名称 含义<br>Databases 数据库的清单<br>Languages 支持语言的清单<br>Logins 登录帐户 ID 的清单<br>RemoteServers 远程 SQL Server 的清单<br>ServerRoles SQL Server 的角色<br>同对 象 一 样 集 合 也 全 部包含在父对象中 在表 7-2 列 出 的对 象集 合中 所有 的对象<br>集合都 属于 SQLServer Core 对象<br>集合实际上是一系列有自己的属性和方法集的对象集合 下面的清单显示包含在<br>Databases 集合 中的 不同 属性 和方 法<br> Application<br> Count<br> Item<br> ItemByID<br> Parent<br> Refresh<br> Remove<br> TypeOf<br> UserData<br>可以看 到 Databases 集合 对象 的属 性都 对应 着数 据库组 的 应用 例如 Count 属性报 告包含在 该集 合中 的 Database 对象的 数量 而 ItemByID 属性 确认 在 Databases 集 合 中指定的<br>Database 对像 因为 所有 的集 合对 象包 含和 管理 多个 对象 所以所有集合的属性和方法都<br>是非常 类似 的<br>相比较 而言 下面 的清 单 列出了 单个 Database 对象的 一些 属性<br> Application<br> CreateDate<br> DataSpaceUsage<br> Defaults<br> FileGroups<br> Name<br> Owner<br> Permissions<br> Rules<br> SpaceAvailable<br> StoredProcedures<br> Tables<br> Views<br>可以看 到 Database 对象 的这 些属 性全 部与 SQL Server 的数 据库 直接 相关 例如 Size<br>属性报告该数据库在磁盘上占用的空间 Owner 属性包含数据库所有者的名称 注意<br>Database 对象 的某 些属 性也 是其 他的 集合 对象 例如 StoredProcedures 属 性是数据 库中 存<br>储过程 的集 合 同样 Tables 属性 是包 含在 该数 据库 中表 的集 合 可以 复 习一下 图 7-2 图<br>7-3 和图 7-4 看看 SQL-DMO 使用的 完整 的对 象和 集 合层<br> 循环集 合<br>为了有效地使 用 SQL-DMO 首先需 要了解如何使 用集合对象 使用下面的代 码 可<br>以完成 在集 合中 进行 循环<br> For Each oDatabase in oSQLServer.Databases<br> Debug.Print oDatabase.Name<br> Next<br>Visual Basic 的 For Each 语 句自动 在集 合的 全部 对象 中循 环 该示 例将 打印 一份 数据 库名 称<br>的清单 这些 数据 库包 含 在 oSQLServer 对象的 Databases 集合 中 在 For Each 块 中 的代码<br>引用了 集合 中的 当前 对象<br> 获取指 定的 集合 对象<br>还需要理解如 何引用集合中的指定对象 根据对象名称或者根据集合 中的序数值 可<br>以引用 集合 中的 单个 对象 例如 根据 名称引 用一 个 Database 对象 可 以使 用 下面的 一条<br>语句<br> oSQLServer.Databases( model )<br> 或者<br> oSQLServer.Databases.Item( model )<br>这两个 示例 是等 价的 它们 都引 用 了在 oSQLServer 对象中的 model 数据 库 因为 Item<br>方法是 默认 的 所以 可以省 略 使用 Item 方法 换 句话说 为了 根据 名称引用单个的 集 合 对象 使用 Item 方法传送包含 该 对象 名称 的字 符串<br>注意 这种 代码 章音暗示使 用了该 集 合对象 的 Item 方法 Item 方法 是 集合 中默认的<br>方法 因此 不 需 要明确 地使 用 oSQLServer.Databases.Item(“model”) Item<br>方法可 以接 受字 符串 或者序 号<br>为了根 据序 号引 用第 一个数 据 库对 象 可以 使用 下面的 语 句<br> oSQLServer.Databases(1)<br>再重复 一遍 这个 代码 暗 示 使用了 Databases 集合 的 Item 方法 在这 个示 例中 Item<br>方法传 送第 一个 序号 值 而不是 传送 包含 数据 库名 称 的字符 串 序数 值 1 返回 oSQLServer<br>对象中 的第 一个 数据 库名称 类似 地 序数 值 2 返回 第二个 数据 库名 称 等等<br>7.3.7 使用 SQL-DMO 访问 SQL Server 数据库<br>在本章 的第 一部 分 概述了 SQL-DMO 的对象层 次结 构 接 下来简 要介绍 了如何使 用<br>一些最 重要 的 SQL-DMO 集合 方法 和属 性 前面我 们提 及 SQL-DMO 具 有数 据访问 功能<br>下面我 们看 一个 用 SQL-DMO 访问 数据 库的 例子<br>Dim objMySvr As SQLDMO.SQLServer<br>Dim objMy DB As SQLDMO.Database<br>Dim objCustTbl As SQLDMO.Table<br>Dim objNewCol As SQLDMO.Column<br>‘connect to server<br>Set objMySvr=New SQLDMO.SQLServer<br>ObjMySvr.Connect”ZHENGDONG”,”sa”,”sql”<br>‘get reference to customers table<br>Set objMyDB= ObjMySvr.Databases(“NorthWind”)<br>Set objCustTbl= ObjMyDB.Table(“Customer”)<br>‘create new column for Email address<br>Set objNewCol=New SQLDMO.Column<br>ObjNewCol.Name=”Email”<br>ObjNewCol.Datatype=”nvarchr”<br>ObjNewCol.Length-<br>ObjNewCol.AllowNulls=Ttrue<br>‘add column to table<br>ovjcustTbl.Columns.Add objNewCol<br>objMySvr.DisConnect<br>程序首 先定 义了 四个 SQL-DMO 对象 objMySvr objMyDB objCustTbl 和 objNewCol<br>然后 objMySvr 的 connect 方法连接到所需的数据库 接着 将 objMyDB 的属性设置为<br>NorthWind 将 objCustTbl 的属性 设置 为 Customer 从而 定位 到表 Customer 最 后利用 Columns<br>的 Add 方法 向表 中插 入列<br>7.4 使用 SQL-DMO 管理 SQL Server 服务 器<br>通过 上面 的例 子我 们了 解 到使用 SQL-DMO 可以 访问 数据 库 但在前面我们说过<br>SQL-DMO 的优势在于实现服务器的 高级管理 功能 在本章的这一部分中 将讨 论如何 实现 SQL-DMO 的管 理功 能 首先介 绍一 个简 单的 例子 来说 明 SQL-DMO 是如 何工 作 的 以<br>便读者 对 SQL-DMO 编程 有个 感性 认识<br>图 7-9 是示 例运 行的 主窗体 上面 主要有 三个 文本 输入 框 txt_SQLServer txt_Login<br>txt_Password 一个 复选框 chk_integrated 和两个 按 钮 cmdConnect cmdCancel 用 户通过<br>输入数 据库 名 帐号 和密码 程序 就会 连接 到选 定的数 据 库 连接 成功 后显 示如图 7-10 所<br>示的消 息框<br> <br> 7-9 7-10 <br>图 程序主窗体 图 连接成功 消息框<br>下面是 程序 的源 代码<br>Private Sub cmdConnect_Click()<br>On Error GoTo ErrorHandler<br> Dim oSQLServer As New SQLDMO.SQLServer<br> Dim bConnected As Boolean<br> bConnected = False<br> oSQLServer.LoginTimeout = 30<br> If chk_integrated.Value = 1 Then<br> oSQLServer.LoginSecure = True<br> oSQLServer.Connect txt_SQLServer.Text<br> Else<br> oSQLServer.Connect txt_SQLServer.Text, txt_Login.Text, _<br> txt_Password.Text<br> End If<br> reponse = MsgBox("Connect Successfully!", vbOKOnly)<br> oSQLServer.DisConnect<br> Exit Sub<br>ErrorHandler:<br> MsgBox (Err.Description)<br> If bConnected = True Then<br> oSQLServer.DisConnect<br> End If<br>End Sub<br>通过前面 的学习 上面的 代码不难看懂 然而就是 这段简单的代码揭示了 SQL-DMO<br>运行的 基本 模 式 下 面看一个 稍微 复杂 些 的 例子 通过这 个例 子我 们 可 以浏 览 SQL Server<br>服务器的 对象 如数据 库 表 视图 存储过程等 这个例 子所具有的功 能已经 初步体 现<br>了 SQL-DMO 编程 的价 值之 所在<br>程序在 Visual Basic 下编译 运行 的结 果如 图 7-11 所示<br>7-11 <br>图 应用程序主窗体<br>这个应 用程 序示 例将 演示使 用 SQL-DMO 所需 的许多 关键 技术 包括 下面 内容<br> 创建 SQLServer 对象<br> 连接到 SQL Server 系统<br>SQL-DMO<br> 使用 的对 象层<br> 使用集 合<br> 从集合 中获 取指 定的 对象<br> 与 SQL Server 系统断开 连接<br>当完成 SQL Server 的名称 LoginID 和 Password 的内容 单击 Connect 按钮 之后 该<br>SQL Server<br>示例应 用程 序就 连接 到指定 的 系统 成功 连接 之后 该示 例应用 程序 列出 授权<br>该用户使 用的全部数据 库 在列表中 自动 填充所显 示的第 一 个数据库中 的表和 视图 存<br>储过程<br>双击任 何一 个 列 表项 修 改所有 互相 依赖 的列 表 例如 双击一个 不同 的 数 据库名 称<br>则使表 视图 及存 储过 程 列表 中的内 容也 会随 之更 新 类似地 双击 Tables 列 表 上的一个<br>Tables<br>不同的 表 视 图及 存储 过 程 列表中的 内容 会随 之更 新 然而 双击 列 表中的一 项并<br>不修改 Databases 列表 因为 在 SQL Server 对象层 次中表的 集 合 在数据 库 的下 面<br>7.4.1 创建 SQLServer 对象并连 接SQL Server<br>该示 例应 用程 序需 要做 的第一件 事情 是创 建一 个 SQLServer 对象 然后连接到 SQL<br>Server 系统 在示例应用程序中 SQLServer 对象 在形式上是由所有对象共享 的 因此<br>Visual Basic Declaration<br>它可以 使用 主 形式 的 节 中 的下述代码<br>option Explicit<br>Dim oSQLServer As SQLDMO.SQLServer<br>Dim oCurrentDB As SQLDMO.Database<br>Dim oCurrentTable As SQLDMO.Table<br>Dim bConnected As Boolean<br>当 oSQLServer 对象创建 之后 可以 使用 该对 象的 Connect 方 法创建到 SQL Server 系统的连 接 在示 例应 用程序 中 当 用户填 充相 应 的 SQL Server 名 称以及 该 SQL Server 的 Login<br>ID 和 Password 之后 单击 Connect 按钮 则启 动 SQL Server 的连接 单击 Connect 按钮执<br>行下面 的代 码 这些 代码在 cmdConnect_Click 子例 程中<br>Private Sub cmdConnect_Click()<br> On Error GoTo HandleError<br> ''If we''re connected, then disconnect and clear lists.<br> If bConnected = True Then<br> oSQLServer.DisConnect<br> bConnected = False<br> cmdConnect.Caption = "&Connect"<br> ''Empty all lists and disable prompts.<br> FillEmptyDatabaseList (False)<br> Exit Sub<br> End If<br> ''Set mouse pointer to "Wait" cursor.<br> frmIdxTest.MousePointer = 11<br> ''If no login name, use NT Integrated security in an attempt.<br> ''to connect.<br> If Not (Len(txtLogin.Text)) Then<br> oSQLServer.LoginSecure = True<br> End If<br> oSQLServer.Connect txtServer.Text, txtLogin.Text, txtPassword.Text<br> bConnected = True<br> cmdConnect.Caption = "&Disconnect"<br> ''Fill databases list. If any databases found, select first and<br> ''fill tables collection.<br> FillEmptyDatabaseList (True)<br> If listDatabases.ListCount > 0 Then<br> listDatabases.ListIndex = 0<br> listDatabases_Click<br> End If<br> frmIdxTest.MousePointer = 1<br> Exit Sub<br>HandleError:<br> frmIdxTest.MousePointer = 1<br> SQLDMOError<br> Exit Sub<br>End Sub<br>Private Sub FillEmptyDatabaseList(bFill As Boolean)<br> listDatabases.Clear<br> If bFill = True Then<br> Dim oDB As SQLDMO.Database<br> For Each oDB In oSQLServer.Databases<br> If oDB.SystemObject = False Then<br> listDatabases.AddItem oDB.Name<br> End If Next oDB<br> End If<br>End Sub<br>oSQLServer 对象的 Connect 方法创建到 SQL Server 系统的连接 正如在<br>cmdConnect_Click 子例 程中 看到 的那 样 Connect 方 法使用 三个 参数 SQL Server 的系统 名<br>称 登录 帐户 ID 和口 令 这些 数值 都 由文本 框提 供 可以 在图 7-11 中看 到 SQL Server 的<br>名称包含在 txtServer.Text 属性中 登录帐户 ID 来自 txtLogin.Text 属性 口令则由<br>txtPassword.Text 属性 提供<br>如果用 户输 入一 个无 效的 SQL Server 名称 或者 其他 无效 的登 录信 息 那么 Connect 方<br>法就会 失败 如果 Connect 方法 失败 就 生成一 个实 时错 误 激活 Visual Basic 的 错 误句柄<br>这将使 cmdConnect_Click 子例 程 的 控制跳到 HandleError 标记 执行 PrintError 函数<br>如果 Connect 方法 成功 那么继 续 cmdConnect_Click 例程并禁止 cmdConnect 按钮<br>防止用 户企 图进 行第 二次连 接 接着 cmdConnect_Click 子例程调用FillEmptyDatabaseList<br>函数 用 SQL Server 系统中 的数 据库 名称 填充 Database 列表 FillEmptyDatabaseList 函<br>数主要 用AddItem 方法 到 连接成功 后 Connect 按钮 变为 Disconnect<br>以上简要介绍 了连接模块的主要内容 具 体细节相信读者在参照注释 语句后 很容易<br>会弄明 白 故此 不再 详述<br>7.4.2 数据 库模 块<br>oSQLServer 对象的 Databases 属 性包 含了用 于连 接 SQL Server 系统的 数据 库名 称集 合<br>在 Command_Connect_Click 子 例程中 可以 看到 使用 Visual Basic 的 For Each 操 作 循环搜<br>索数据库名称的集合 把每一 个 数 据 库 名 增 加到数据 库的列 表中 For Each 的 每一次 循环<br>都寻址 Databases 集合 中不 同的 Database 对象 例如 For Each 循 环第一次 执行 时 Databases<br>集合中 第一 个 Database 对象 将是 当前 的对 象 第二 次执 行 For Each 循环 时 Databases 集<br>合中的 第二 个 Databases 对象 将是 当前 的对 象 在 For Each 循环 内 使用 AddItem 方法把<br>包含在 当前 oDatabase 对象的 Name 属性 中的 数据 库 名称增 加到 List_Database 列表中 对<br>包含在 Databases 集合 中的 每一 个对 象 For Each 循 环执行 一次<br>为了自 动填 充 Tables 列表 Stored Proc or Views 列表 cmdConnect_Click 子例程执 行<br>listDatabases_Click 子例 程 该 子 例程可以 从给 定 的数据 库中 表的 清单<br>7.4.3 表模 块<br>在示例 应用 程序 中 检 索 数据库 表信 息的 代码 要么 是 在 cmdConnect_Click 子 例 程的末<br>尾自动执 行 要么当用 户双击数据库 清单中的某一个 数据库 名称时执行 这两 种 情况都 执<br>行listDatabases_Click 子例 程 如下 所示<br>Private Sub listDatabases_Click()<br> ''If no item selected, simply return.<br> If listDatabases.ListIndex = -1 Or listDatabases.ListCount = 0 Then<br> Set oCurrentDB = Nothing<br> Set oCurrentTable = Nothing<br> FillEmptyTableList (False)<br> Exit Sub End If<br> ''Set to "Wait" pointer.<br> frmIdxTest.MousePointer = 11<br> ''Get the selected database.<br> Dim oDB As SQLDMO.Database<br> Set oDB =<br>oSQLServer.Databases(listDatabases.List(listDatabases.ListIndex))<br> ''If the database selected references the current one, return. Otherwise<br> ''get the list of tables from the newly selected database.<br> If oCurrentDB Is Nothing Then<br> Set oCurrentDB = oDB<br> Else<br> If oCurrentDB.Name = oDB.Name Then<br> Exit Sub<br> End If<br> Set oCurrentDB = Nothing<br> Set oCurrentDB = oDB<br> End If<br> ''Get the list of tables and select the first one.<br> FillEmptyTableList (True)<br> If listTables.ListCount > 0 Then<br> listTables.ListIndex = 0<br> End If<br> ''Reset pointer.<br> frmIdxTest.MousePointer = 1<br>End Sub<br>Private Sub FillEmptyTableList(bFill As Boolean)<br> listTables.Clear<br> If bFill = True Then<br> Dim oTable As SQLDMO.Table<br> For Each oTable In oCurrentDB.Tables<br> If oTable.SystemObject = False Then<br> listTables.AddItem oTable.Name<br> End If<br> Next oTable<br> End If<br>End Sub<br>在 listDatabases_Click 子例程中首先创建一个 SQLDMO 表对象的实例 然后判断<br>databases 列表 是否 为空 若不为 空则 调用 FillEmptyTableList 函数填充 Tables 列表<br>正如使 用 Databases 集合 一样 使用 Visual Basic 的 For Each 操 作列出 Tables 集合的 成<br>员 For Each 操作 循环 搜 索 SQL Server 表名 称的 集合 这 些内 容表名在 oSQLServer 对象<br>的 Databases 集合 的某 个指 定 成员 中 指定 的 Database 对 象名称包 含在 sDatabase 字符串 变<br>量中 该变 量被 传送 到 oSQLServer.Databases 对象 的 addItem 方法中(addItem 方法是 Database<br>对象的 默认 方法 因此 不 需要 在代码 中明 确地 指出) 在 For Each 循环 内 每一 个 Tables 集<br>合的成 员用 Add Item 方法增加到 listTables 列表 中7.4.4 存储 过程 视图 模块<br>此模块的结构 及实现方法大体类似于前面 讲的表模块 阅读时请参照 前面的介绍 这<br>里就不 详细 介绍 了 只 给 出程序 清单<br> Private Sub listTables_Click()<br> ''If no database or no tables, clear stored proc/view list and return.<br> If oCurrentDB Is Nothing Or listTables.ListIndex = -1 Or<br>listTables.ListCount = 0 Then<br> Set oCurrentTable = Nothing<br> FillEmptySProcList (False)<br> End If<br> ''Set to "Wait" pointer.<br> frmIdxTest.MousePointer = 11<br> ''Get the selected table.<br> Dim oTable As SQLDMO.Table<br> Set oTable = oCurrentDB.Tables(listTables.List(listTables.ListIndex))<br> ''If the table selected references the current one, return. Otherwise<br> ''get the list of dependent stored procedures and views.<br> If oCurrentTable Is Nothing Then<br> Set oCurrentTable = oTable<br> Else<br> If oCurrentTable.Name = oTable.Name Then<br> Exit Sub<br> End If<br> Set oCurrentTable = Nothing<br> Set oCurrentTable = oTable<br> End If<br> ''Get the list of dependent stored procedures and views.<br> FillEmptySProcList (True)<br> ''Reset pointer.<br> frmIdxTest.MousePointer = 1<br>End Sub<br> Private Sub FillEmptySProcList(bFill As Boolean)<br> listSProcs.Clear<br> If bFill = True Then<br> Dim oQR As SQLDMO.QueryResults<br> Set oQR = oCurrentTable.EnumDependencies(SQLDMODep_Children)<br> ''If the object type is stored procedure or view, add it to<br> ''the list of testable objects.<br> Dim nRow As Integer<br> Dim oType As SQLDMO.SQLDMO_OBJECT_TYPE<br> For nRow = 1 To oQR.Rows<br> oType = oQR.GetColumnLong(nRow, 1)<br> If oType = SQLDMOObj_StoredProcedure Or oType = SQLDMOObj_View Then<br> listSProcs.AddItem oQR.GetColumnString(nRow, 2)<br> End If Next nRow<br> End If<br> ''Clean up dependent objects.<br>End Sub<br>7.4.5 程序 的其 他模 块<br>另外 本程序还有其他模块 如窗体的装载 卸除等 这些 都比较简单 这里 只给出<br>它们的 程序 清单<br>Private Sub cmdExit_Click()<br> Form_Unload (0)<br> End<br>End Sub<br>Private Sub Form_Load()<br> bConnected = False<br> Set oSQLServer = New SQLDMO.SQLServer<br> oSQLServer.LoginTimeout = 15<br>End Sub<br>Private Sub Form_Unload(Cancel As Integer)<br> On Error Resume Next<br> If bConnected = True Then<br> oSQLServer.DisConnect<br> End If<br> oSQLServer.Close<br> Set oSQLServer = Nothing<br>End Sub<br>7.4.6 SQL-DMO 的错 误句 柄<br>SQL-DMO 方法 生成 的 错 误可以 使用 Visual Basic 的 标准 错误句柄来获得 如果 SQL-<br>DMO 方法 生成 了某 个错 误 并且 Visual Basic 的 错误处 理句柄实现 那 么用 户将会 看到 一<br>个 Visual Basic 的运 行时 错误 并且 该应 用程 序将 被 终止<br>提示 利用Visual Basic 的内 置错 误句 柄来 俘获 任 何 SQL-DMO 错误 并 且 防止没 有考<br>虑到的 应用 程序 错误<br>在示例 SQL-DMO 应用 程序 中提 供的 所有 例程 都使 用通 用的 SQLDMOError 例程 如<br>下所示<br>Public Function SQLDMOError ()<br> Dim errString As String<br> If Err.Number <> 0 Then<br> errString$ = Err.Source & " Error " & Err.Number & ": " & Err.Description<br> MsgBox errString$<br> End If<br>End Sub<br>SQLDMOError 函数是一个相对简单的函数 它向用户显示一个消息框 提供了有关<br>所碰到 的错 误情 况的 详细信 息 使用 Visual Basic Err 对象 可以 显示 SQL-DMO 错误 这<br>种 Err.Source 属性 包含 了 引起该 错误 的 SQL-DMO 组 件名称 从 Err.Number 属 性 中减去 常数 vbObjectError 得到 SQL-DMO 的错误 号 Err.Description 属 性包含 该错 误的 文本 描述<br>7.5 小 结<br>正如在 代码 示例 中看 到的那 样 DMO 把 SQL Server 的管 理功 能展 示给了 Visual Basic<br>并且其 OLE 方法 使其 易于 在 Visual Basic 和其 他的 OLE 兼 容的应 用程 序中 使用第8 章 在C 中使 用嵌 入SQL 访问SQL Server 数据库<br>嵌入式 SQL Embedded SQL 是 SQL Server 提供 的用 于开发 客户 端应 用程 序的专 用<br>开发工 具 目前 只有 C 语言 版本 Embedded SQL for C 使用 它能 够在 C 语言程 序 中 嵌<br>入 Transact-SQL 语句进行数 据 库操 作 与 ODBC 和 DB Library 相比 嵌入 式 SQL 是通过<br>在 C 语言 程序 中自 接使 用 Transact-SQL 的语句而 不是使 用 函数 调用 来进 行数 据库 应用 程序<br>开发 因而它是一种简单高效的程序开发工具 它 使 用预编 译技 术将 C 源 程序中的 SQL<br>语句转 换为 对运 行库 的函数 调 用 运行 库用 调用 DB-Library 来访问 SQL Server 数据库 由<br>于在 C 源程 序中 直接 包含 了 SQL 语句 所以 嵌入 式 SQL 只能用于 开发 SQL Server 专用<br>的数据 库应 用程 序 适 用 于数据 库结 构明 确并 已变 化 不大的 应用 环境<br>8.l 嵌入式 SQL 应 用 程序 开发环 境<br>使用 嵌入 式 SQL 能 够开发多种环境下的 SQL Server 数 据库 应 用程 序 如 Windows<br>2000 Windows NT 和 Windows 95/98 下的 32 位应 用程 序 以及 Windows 3.x 和 MS-DOS<br>下的 16 位应 用程 序等 开发 不同 环境 下的 嵌入 式 SQL 应用程 序所 需要的 系统 环境 和编 译<br>器如表 8-l 所示<br>表8-1 嵌入式 SQL 开发环境<br>应用开发环境 操作系统 C 编译器<br>Windows NT 3.51 Microsoft Visual C++ 2.0<br>Windows NT 及其以上版本 及以上版本或其它兼容产品<br>Windows 95 Windows 95 Microsoft Visual C++ 2.0 及以上版本或其它兼容产品<br>Windows Windows 3.1 或 Windows for Microsoft Visual C++ 1.52 及以上版本或其它兼容产<br>品<br>Workgroups 3.11<br>MS-DOS MS-DOS 6.22 及其以上版本 Microsoft Visual C++ 1.52 及以上版本或其它兼容产<br>品<br>嵌入式 SQL 应用程序 在使用 常 规 C 编译器编 译 链 接之前 需要 用嵌 入式 SQL 预编<br>译器进 行预 处理 预编 译 器将源 程序 中的 嵌入 式 SQL 语句 转换 成能 被常 规 C 编译器 识 别 的<br>函数调 用 然后 C 编译 器才 能将 转换 后的 源程 序编 译成 可执 行文 件<br>使用嵌 入式 SQL 开发应用程 序 时 除需 要以 上系 统环境 和 预编 译器 之外 还需 要 C 语<br>言 头 文件 和函 数 库 以及 预 编译 和 运 行 时 使用的动态 链 接库 等 这些 文件 及 其 作 用如 表 8-<br>2 所示<br>表 8-2 中说 明的 函数 库和动 态 链接 库均是 用于 开发 32 位 嵌入式 SQL 应 用程序所 需要<br>的支持 文件 SQL Server 2000 没有 提供 开发 16 位应 用程序 所需 的支 持库 文件 如果需 要<br>开发运 行于 Windows 3.x 和 MS-DOS 下的 嵌入 式 SQL 应用程序 可从 微软 公司 经销 商处<br>得到 16 位代 码的 支持 库 文件<br>表 8-2 嵌入式 SQL 编程所需支持文件<br>文件名 作用<br>NSQLPREP.EXE Windows NT 和 Windows95 下的预编译器<br>SQLAIW32.DLL Windows NT 和 Windows95 下的预编译器支持动态链接库<br>SQLAKW32.DLL Windows NT 和 Windows95 下的嵌入式 SQL 应用程序运行时支持的动态链接库<br>SQLCA.H 定义SQLCA 数据结构<br>SQLDA.H 定义SQLDA 数据结构<br>CAW32.LIB Windows NT 和 Windows95 下的 SQLCA 输入库文件<br>SQLAKW32.LIB Windows NT 和 Windows95 下运行时支持输入库文件<br>Microsoft 公司提 供了 以下几 种 形式 的函 数库 以支 持 不同环 境下 的嵌 入式 SQL 程序的<br>运行<br> 多线程 动态 链接 库 NTWDBLIB.DLL 支持 Windows NT 和 Windows 95 编程<br> 动态链 接库 MSDBLIB3 DLL 支持 Windows 外 境下编 程<br>在具备 了以 上环 境和 支持文 件 后 就能 够使 用嵌 入式 SQL 开发 SQL Server 2000 数据<br>库应用 程序 了<br>8.2 嵌入式 SQL 数据 类型<br>嵌入式 SQL 是专用的 SQL Server 开发工具 所以 它 提供了 C 语 言数据类型 与 SQL<br>Server 数据类 型之 间的 对应 关系 但嵌 入式 SQL 不支持 Unicode 数 据类型 即 SQL Server<br>中的 nvarchar nchar 和 ntext 等数 据类 型 由于 SQL Server 中的 datetime smalldatetime<br>money smallmoney decimal 和 numeric 数据类型 在 C 语 言中没有 相应 的数 据 类型 所以<br>嵌入式 SQL 提供了 某些 C 数据 类型 到这 些数 据类 刑 的转换 C 语言 数 据 类型与 SQL Server<br>数据类 型间 的对 应关 系如表 8-3 所示<br>表 8-3 C 语言数据类型与SQL Server 数据类型间的对应关系如<br>C 数据类型 SQL Server 数据类型 可转换成 datetime 可转换成 money 或 可转换成<br>或smalldatetime smallmoney decimal 或<br>numeric<br>Short Smallint 否否否<br>否否否<br>Int Smallint<br>Long Int 否否否<br>否否否<br>Float Real<br>Double Float 否否否<br>Char Varchar[x] 是是是<br>是是是<br>Viod p Binary<br>Char byte tinint 否否否<br>如果接 收输 出结 果的 C 语言 应用 程序 的变 量的 数据 类型 宽度 较短 时 结果 数据将 被 截<br>断 同样 如果 接收 输入数 据 的 SQL Server 列的宽度 较 短 输入 数据 也将 被截断 另外<br>由于在 SQL Server 存储过程 中禁 止使 用 text 数据 类型 同此 在 SQL 语句中 不能使 用 超过<br>255 个字 节的 C 字符 串<br> 字符数 据交 型 C 语言 中的 char 数据 类型 可对 应 SQL Server 的 char varchar 或<br>text 数据类 型 从 C 到 SQL Server 数据 类型 的转 换过 程中 数据 被拷 贝或 截断<br>SQL Server 数据宽度 较短 从 SQL Server 到 C 数 据类型的 转换 过程 中 数据<br>被拷贝 或截 断 C 数据 宽度 较短 并且 后面 跟一 个 NULL 字符<br> 日期 时间 型数 据 C 语言 没有 日期 时间 数据 类型 SQL server 的 date 或 time<br>列按指 默认 的日 期格 式被转 换 成 C 字符串 如<br> mm dd yyyy hh mm:ss [am|pm]<br>同样 可使 用 C 字符 串向 SQL Server 传递日期 数据 字符 串中 的日 期格 式必 须 能被 SQL<br>Server 识别<br>8.3 嵌入式 SQL 语法<br>嵌入式 SQL 程序就是 Transact-SQL 语句和 C 语句的 组合 体 在 C 语言 程 序 中嵌入 SQL<br>语句要 符合 一定 的语 法规则 SQL Server 的嵌入式 SQL 语法结合 了 IBM 标准 ANSI 嵌<br>入式 SQL 语法 和 Transact-SQL 语法规则<br>8.3.l 嵌入 式 SQL 语 句 及保留 字<br>在 C 源程 序中 任何 可以 放置 函数 的地 方均 能够 包含 嵌入 式 SQL 语句 也就 是说嵌 入 式<br>SQL 语句相当于 C 中的一 条语句 每一个 嵌 入 式 SQL 语 句都以 前导 关键 字 EXEC SQL<br>开头 以分 号 结束 将嵌 入式 SQL 语句与 C 语 句分开 一条 嵌入 式 SQL 语 句 的最大<br>长度是 8191 个字 符 16 位应 用程 序 和 19999 个字 符 32 位 应用程序 当嵌 入式 SQL<br>语句中的字符串一行 写 不下时 可 用反斜线 连接多 行字 符串 SQL 字 符 串需 用单 引<br>号括起 来 如<br>EXEC SQL INSERT INTO TEXT132 VALUES (''TEST 192 IS THE TEST FOR THE R\<br>ULE OF THE CONTINUATION OF LINES FROM ONE LINE TO THE NEXT LINE.'');<br>也可以 将嵌 入式 SQL 语句和 C语句 写在 同一 行上 如<br>EXEC SQL COMMIT TRAN; printf("\n");<br>嵌入式 SQL 的关键字 和语句 是大小 写不 敏感的 例如 下面几 个嵌 入式语句 具有相同<br>的功能<br>EXEC SQL CONNECT TO<br>exec sql connect to<br>然而嵌 入式 SQL 语句 所使用 的名字 比 如游标 预处理 语句和 连接 等名字是 大小写敏<br>感的 在 名字的定义和 使用中必须保 持一致的大小写 例如 下面语句定 义了两 个不同 的<br>游标<br>DECLARE CUR_NAME CURSOR<br>DECLARE cur_name CURSOR<br>Transact-SQL 的关键字 null 在嵌 入式 SQL 程序中不 能大 写 否则 会与 C 语言 关 键字NULL冲突 在 32 位应 用程 序中 嵌入 式 SQL 的关键 字 delete 和 Transact-SQL 的关键字 in 也<br>不能大 写 否则 与 32 位 Windows 定 义的常 量冲 突<br>下面 是嵌 入式 SQL 的保留字 这些保留字和 Transact-SQL 保留字均不能用作 C 语<br>言程序 中的 变量 名或 函数名<br>表 8-4 嵌入式 SQL 的保留字<br>APPLICATION FETCH OPTCCVAL<br>CALL FETCHBUFFER OPTION<br>CLOSE FOUND QUERYTIME<br>CONCURRENCY GET READONLY<br>CONNECT HOST SCROLLOPTION<br>CONNECTION IMMEDIATE SECTION<br>CUR_BROWSE INCLUDE SQLCA<br>CUR_STANDARD INDICATOR SQLDA<br>CURRENT KEYSET SQLERROR<br>CURSOR LOCKCC SQLWARNING<br>CURSORTYPE LOGINTIME USER<br>DESCRIBE MIXED USING<br>DESCRIPTOR NOT WHENEVER<br>DISCONNECT OF WORK<br>DYNAMIC OPEN<br>FORWARD OPTCC<br>8.3.2 静态 SQL 语句 和动 态 SQL 语句<br>嵌入式 SQL 支持静 态 SQL 语句和 动态 SQL 语句 静态 SQL 语句在 程序 设计时 各 个<br>SQL<br>部分均 已确 定 而动 态 语句在 应用 程序 执行 时才被 建 立和 运行<br>静态 SQL 语句是嵌入到源程序中的一条完整的 Transact-SQL 语句 一个完整的<br>Transact-SQL 事务 包 括变量定 义 流控制语 句和 存储 过 程 调用 可被 放入 一条 静态 SQL<br>语句中 静态 SQL 语句可包 含 C 语言变量来 输入 和 输出数 据 在应 用程 序中 定 义的变 量可<br>被 C 和嵌 入式 SQL 访问 当一 条静 态语 句使 用 C 语 言变量输 入数 据时 在静 态语句 运行<br> SQL SQL<br>前变量 的值 被插 入到 语句中 同样 在 语句执 行 完后输 出值 被填 入输出 变 量中<br>如果 SQL 语句 返回的结 果集中数据行 多于 一行时 则只有第一行 的数 据被读取 到变 量之<br>中 而其 余数 据行 的数 据 被抛弃 对于 多行 数据 的结果 集 只能 使用 游标 来处 理<br> 例如 下面 语句 即为一 条 静态 SQL 语句<br>EXEC SQL SELECT au_fname INTO :szFirstName<br> FROM authors WHERE au_lname = :szLastName;<br>在编译 阶段 静态 SQL 语句可 被编 译成 访问计 划 中的存 储 过程 或以 动态 SQL 语句<br>的方式 执行 访问 计划 access plan 是存 储过 程的 集合 其中 的每 一个 存储 过程 对应 一条<br>静态 SQL 语句 使用 嵌入式 SQL 的预 编译 器 可以 连接 到 SQL Server 数据库 并建 立访 问<br>计划 此 应用程序被重 新编译时 程 序模块对应的旧 的存储 过程被删除 并被 新 的存储 过<br>程所取 代 如果 在编 译阶段 SQL Server 数据 库是 无效 的 则可 用嵌 入式 SQL 预 编 译器生成绑定 文件 绑定 文件 是 Transact-SQL 批命令文件 其中 包含 了建 立访 问计 划 中存储 过程<br>所使用 的 Transact-SQL 命令 这时 应在 运行 嵌入 式 SQL 应 用程序之 前连 接 SQL Server<br>并使用 osql 等工 具执 行绑 定文 件 在 SQL Server 数据库中 生成 应用 程序 的访 问计 划 当<br>静态 SQL 语句中只包含单个事务管理命令 如 BEGIN TRANSACTION COMMIT<br>TRANSACTION ROLLBACK TRANSACTION 或 SAVE TRANSACTION 时 此静态 SQL<br>语句不 被编 译成 访问 计划 而是 在应 用程 序执 行时 按 动态 SQL 语 句的方式 执行<br>动态 SQL 语句不 是一 条完整 的 Transact-SQL 语句 其中 某些 部分 需要在 应 用程序 运 行<br>时才能 提供 动态 SQL 语句可 被存 储在 程序 变量 中 在程序 运行 时修 改 动态 地生 成需 要<br>的 SQL 语句 动态 SQL 语句中 使用 问号 作为 参数 标志 用来 说明 需要 应用 程序 输<br>入参数 例如<br>DELETE AUTHORS WHERE AU_FNAME AND AU_LNANE<br>在程序中 可使 用 PREPARE EXECUTE 和 EXECUTE IMMEDIATE 嵌入 式 SQL<br>语句来 处理 一条 动态 SQL 语句 通常 首先 用 PREPARE 语 句准备 动态 SQL 语句 然后再<br>用 EXECUTE 语句 执行 之<br>8.3.3 宿主 变量<br>在嵌入 式 SQL 中 用宿 主 变量来 处理 数据 的输 入 输出 宿主 变量 是标 准的 C 应用程<br>序变量 当 数据 库对 象 的个 数 和数 据类 型已知时 可 以使用 宿主 变量 比如 在静态 SQL 语<br>句中使 用宿 主变量来 输入和 输出数 据 在动态 SQL 语 句中结 合宿 主变 量和 参数 标 志<br>来进计 动态 的数 据处 理<br>宿主 变量 在使 用之 前必 须由嵌入 式 SQL 定义部 分定 义 宿主 变量 定义 部分 以 BEGIN<br>DECLARE SECTION 语 句开始 以 END DECLARE SECTION 语 句结来 定义 部分 不能嵌 套 如<br> EXEC SQL BEGIN DECLARE SECTION<br> int nID<br> unsigned short usNumber<br> char szName 3O<br> EXEC SQL END DECLARE SECTION<br>宿主变 量名 称的 长度 不能超 过 30 个字符 在程 序中允 许 定义 C 变 量的地力均 可 以定 义<br>宿主变 量 已 定义 的宿 主 变量的 使用 范围 遵循 C 语言 的变量 使用 规则<br>在嵌入 式 SQL 语句中 使用宿 主变量 时 变量名前 必须加 冒号 用 来 区分 同名的宿<br>主变量 和数 据库 表 列名 下面 的语 句说 明宿 主变 量 的使用 方法<br>EXEC SQL<br>SELECT price INTO :price:price nullflag<br>FROM titles<br>WHERE au_id = "mc3026"<br> 因为 C 语言 不支 持空值 null 嵌入式 SQL 提 供宿主 指示 变量 来处 理数 据 库的空 值<br>所以 在嵌 入式 SQL 应 用 程序中 可使 用宿 主变 量和 宿 主指示 变量 来表 示一 个 SQL 值 当宿<br>主变量 为 NULL 时 它的 指示 变量 为-l 当 宿上 变量不 为 NULL 时 它的指 示变 量 的值为 宿<br>主变量 数据 的最大长 度 嵌入 式 SQL 语句 中将 指示 变 量直接 放在 对 应的宿 主变 量 后面 两<br>者间以 冒号 分隔 例如 下 面语句 中变 量 Address 利 Address_null 分别为 宿中变 量 和 宿上指示 变量<br>EXEC SQL UPDATE authors SET address=:Address:Address_null<br>也可在 指示 变量 的前 面加上 关 键字 INDICATOR 说明 其 后是一 个指 示变 量 例如<br>EXEC SQL UPDATE authors SET address=:Address INDICATOR:Address_null<br>在上面 的例 子中 当Address_null 的值 为-1时 嵌入 式 SQL 将 语句转换 为<br> EXEC SQL UPDATE Authors SET address=null<br> 在搜 索条 件中 不能 使 用指示 变量 可 以使 用下 面 方法来 搜索 空值<br> If Address.null=-l<br> EXEC SQL DELETE FROM authors WHERE address is null<br> else<br> EXEC SQL DELETE FROM authors WHERE address= address<br> 在 使用 宿户 变 量 时要 注意它与 其对 应的 SQL Server 数 据类 型是否 匹配 否 则 可能会 引<br>起数据 丢失<br>8.3.4 嵌入 式 SQL 的 主 要数据 结构<br>嵌入式 SQL 应用程序 中 主要使 用两 个数 据结 构 SQLCA 和 SQLDA<br> SQL Serve 使用 SQLCA SQL communications area 即 SQL 通信区 结 构 为嵌入 式 SQL<br>应用 程序捕获 和报告运 行 时 错误 应用程 序可查看 SQLCA 结构的错误域和 状态指示 符来<br>确定一条嵌入式 SQL 语句运行是否成功 SQLCA 结构中最重要和最广泛使用的城是<br>SQLCODE 变量 每当 SQL Server 运行一 条嵌 入式 SQL 语句 时 它将 设置 SQLCODE 变<br>量来指 示最 后一 条 SQL 语句是 否成 功执 行 SQLCODE 的值 为 0 说 明嵌入式 SQL 语句已<br>经成功 执行 否则 表明 在 执行嵌 入式 SQL 语 句 时产 生 了警告 或错 误信 息 嵌入 式 SQL 也<br>支持 ANSI 标准 的 SQLSTATE 代码 ODBC 错 误代码 SQLCA 结 构中的 SQLSTATE 域<br>表明所 产生 的警 告或 错误消 息 对应 的 SQLSTATE 代码 SQLSTATE 指 示的错 误有两 种<br>由 DB-Library 或嵌 入式 SQL 产生的 错 误 以及 由 SQL Server 产生的错误 SQLSTATE<br>代码与 SQLCODE 值相 对应<br>当编译时用来传递数据的宿主变量的个数或数据类型不确定时 嵌入式 SQL 使用<br>SQLDA SQL descriptor area 即 SQL 描述 区 结 构 来代替 宿主 变量 SQLDA 结 构 包含了<br>每个输入 参数或输出列 的描述信息 即名 称 数据 类 型 长 度以及指向输 入输出 缓冲区 的<br>指针等 SQLDA 结 构可与 DESCRIBE 或 PREPARE INTO 语 句联合使 用来 从一 条预 处理<br>检索语 句中 接收 数据 在静 态 SQL 语句 中不 能使 用 SQLDA 结构 而 只 能在动 态 SQL 语<br>句或游 标中 使用 SQLDA 结构 下 面分别 介绍 SQLCA 和 SQLDA 的结 构<br>8.3.5 SQLCA<br>嵌入式 SQL 使用 SQLCA 数据 结构 来捕 获并 报告 SQL 语句运 行时 所产 生的 错误消 息<br>对 SQL 命 令执行状态 的 检测 是通过 检 查 SQLCA 数 据结构中 的 各 个域来完 成 的 SQLCA<br>数据结构中的各个域反映了 SQL 命令的状态以及错误代码 错误信息等信息 它是 在<br>SQLCA.H 头文 件中 定义 的 由嵌 入式 SQL 预 编译 器 自动包 含到 C 源 文件中 SQLCA 结<br>构的定 义如 下<br>// SQL Communication Area – SQLCA 数据结 构<br> typedef struct sqlca<br> {<br> unsigned char sqlcaid[EYECATCH_LEN]; // SQLCA 标志 为 ''SQLCA ''<br> long sqlcabc; // SQLCA 字节 数大 小为 136<br> long sqlcode; // SQL 返回码<br> short sqlerrml; // SQLERRMC 长度<br> unsigned char sqlerrmc[SQLERRMC_SIZ]; // 错误信息<br> unsigned char sqlerrp[8]; // 调试信息<br> long sqlerrd[6]; // 调试信息<br> unsigned char sqlwarn[8]; // 警告标志<br> unsigned char sqlext[3]; // 保留<br> unsigned char sqlstate[5]; // 新成员<br> } SQLCA;<br>SQLCA 数据 结构 的各 个域的 含 义说 明如 下<br> Sqlcaid 和 sqlcabc 分别为 SQLCA 结构标志和结构字节长度 其值分别为<br>SQLCA 和 136<br> Sqlcode SQL 命令的返 回代 码 表明 了 SQL 命令的执 行 状态 SQL Server 每执<br>行一 条 SQL 语句 都要 设置 sqlca->sqlcode 的值 来 指 示 SQL 语句执行状态<br>sqlca->sqlcode 的值 为 0 表明 命令 执行 正确 值为 1 表明 SQL 语 句已执行 但有<br>异常情 况发 生 值为 100 表明在 游标 中执 行了 一条 FETCH 语句 但已 没有 记录<br>行可取 值为 负表 明 SQL 语句没有 执行 这可 能是 由 于应用 程序 数据 库 操作<br>系统或 网络 发生 错误 所致<br>为了简化对 sqlca->sqlcode 的引用 嵌入式 SQL 定义了 SQLCODE 变量 其值与<br>sqlca->sqlcode 的值 是一 样的 嵌入 式 SQL 预 编译 器 自动在 包含 main 或 WinMain<br>函数的.sqc 源程 序中 插入 SQLCODE 变量 的定 义语 句 在其 他所 有的.sqc 源 程序中插 入如<br>下语句 来引 用 SQLCODE 变量<br>extern long SQLCODE;<br>如果所 有.sqc 源程 序中 都 没有包 含 main 或 WinMain 函数 则应 该杂 某一 C 源<br>程序模 块中 明确 定义 SQLCODE 变量<br> Sqlderrml 和 sqlerrmc 分 别 说 明嵌入式 SQL 错 误 信息 的 长度和错误信息文本<br>sqlderrmc 域只保存 错误 信息 文本 中的 前 70 个字符 超过部 分将 被截 断 嵌入 式<br>SQL 定义了 如下 宏来 简化应 用 程序 对 sqlca->sqlderrml 和 sqlca->sqlderrmc 的引用<br>#define SQLERRMC sqlca->sqlerrmc<br>#define SQLERRML sqlca->sqlerrml<br> sqlerrp 为嵌 入式 SQL 的保留域 用于 内部 调试<br> sqlerrd 为 6 个整数型 的状态代 码 Sqlca->sqlerrd[0] 为 SQL Server 的错误号<br>Sqlca->sqlerrd[ 1] 为 SQL Server 错误的 严重 级别 Sqlca->sqlerrd[2] 为错 误影<br>响的记 录数 其它 未说 明 的代码 为嵌 入式 SQL 所保 留 嵌入 式 SQL 定 义了几个 宏<br>来简化 对Sqlca->sqlerrd 各个 域的 引用<br>#define SQLERRD1 sqlca->sqlerrd[0]<br>#define SQLERRD2 sqlca->sqlerrd[1]<br>#define SQLERRD3 sqlca->sqlerrd[2]<br>#define SQLERRD4 sqlca->sqlerrd[3]<br> sqlwarn 为嵌 入式 SQL 警告 标志 sqlca->sqlwarn[0]为 所有警告 域的 摘要 指 示<br>sqlca->sqlwarn[ 1] 值为 W 表示 输出 的字 符串 信息被 截 断 sqlca->sqlwarn[3] 值<br>为 W 表示 数据 表的 列数 与宿 主变 量的 个数 不匹 配 其它 未说 明的 域为 嵌入 式 SQL<br>所保留 嵌入 式 SQL 定 义 了如下 几个 宏来 简化 对 Sqlca->sqlwarn 各 个域的引 用<br>#define SQLWARN0 sqlca->sqlwarn[0]<br>#define SQLWARN1 sqlca->sqlwarn[1]<br>#define SQLWARN2 sqlca->sqlwarn[2]<br>#define SQLWARN3 sqlca->sqlwarn[3]<br>#define SQLWARN4 sqlca->sqlwarn[4]<br>#define SQLWARN5 sqlca->sqlwarn[5]<br>#define SQLWARN6 sqlca->sqlwarn[6]<br>#define SQLWARN7 sqlca->sqlwarn[7]<br> sqlstate 为运行 时产 生的 ANSI 标准 SQLSTATE 错误 代码<br>在嵌入 式 SQL 应用程 序中 通过 检查 SQLCODE 变 量来确定 SQL 语 句的执 行状 态<br>如果发 生警 告或 错误 可检 查 SQLWARN SQLERRD1 来 确定发生 何种 错误 SQLERRMC<br>包含了错误 处 理 信息 SQLERRD3 指出错 误 影 响的记录 数 嵌入式 SQL 应 用 程序根据这<br>些信息 可以 对错 误做 出相应 的 处理<br>8.3.6 SQLDA<br>SQLDA 数据 结构 是在 SQLDA.H 头文 件中 定义 的 由 嵌入式 SQL 预 编译器 自动 包括<br>到 C 源文 件中 SQLDA 结构定 义如 下<br>// SQL Descriptor Area – SQLDA 数据 结构<br> struct sqlda<br> {<br> unsigned char sqldaid[8]; // SQLDA 标志 为 ''SQLDA ''<br> long sqldabc; // SQLDA 字节 数大 小 为 16+44SQLN<br> short sqln; // SQLVAR 元素 个数<br> short sqld; // SQLVAR 被使 用的 元素个 数<br> struct sqlvar // SQLVAR 元素 的数 据结构<br> {<br> short sqltype; // 变量类型<br> short sqllen; // 变量长度 最大 不能 超过 32K<br> unsigned char FAR sqldata; // 指向变量数 据的 指针<br> short FAR sqlind; // 指向 null 变量 的指 针<br> struct sqlname // 变量名<br> {<br> short length; // 名称长度 为 1 到 30<br> unsigned char data[30]; // 变量或 数据列 表 的名 称<br> } sqlname;<br> } sqlvar[1];<br> };<br>表 8-5 sqltype 数据类型代码表<br>Sqltype 代码 数据类型描述 对应的 SQL Server 数据 例子<br>类型<br>26<br>392/393 字节日期时间类型 其格 Datetime,smalldatetime char date1[27] =<br>式必须满足使用 DB-Library Mar 7 1988 7:12PM;<br>函数 dbconvert 转换字符串到<br>日期时间函数的要求<br>444/445 二进制类型 binary, varbinary, image, char binary1[4097];<br>timestamp<br>注 444/445 自动用作列<br>的输出类型<br>长度小于 255 的字符串类型 char, varchar, 或 text<br>452/453 char mychar[255];<br>不能自动以 null 作为结束符<br>char, varchar, text<br>456/457 带长度前缀的长字符串结构 或 struct TEXTVAR<br>不能自动以 null 作为结束符 {<br> short len;<br> char data[4097];<br>} textvar;<br>null char, varchar, text<br>462/463 带 结束符的字符串类型 或 char mychar1[41];<br>char mychar2;<br>8 字节浮点数<br>480/481 float, real, int, smallint, double mydouble1;<br>tinyint, decimal, numeric,<br>money, smallmoney<br>482/483 4 字节浮点数 float, real, int, smallint, float myfloat1;<br>tinyint, decimal, numeric,<br>money, smallmoney<br>496/497 4 字节整数 int, smallint, tinyint, bit long myint1;<br>500/501 2 字节整数 smallint, tinyint, bit short myshort1;<br>SQLDA 结构 中各 个域 的含义 说 明如 下<br> Sqldaid SQLDA 结构 标志 符 该字 段不 能用 于 FETCH OPEN EXECUTE 语句<br>中<br> Sqldabc SQLDA 结构的字 节 长度<br> Sqln SQLDA 数据 结构 中分 配的 sqlvar 元素的 总个 数 每一 个 sqlvar 结构 描述了<br>一 个 数据 库列 表的 名字 数据 类 型 长度 数据 缓冲区等 信息 Sqln 值 等 于输入<br>参数或 输出 列的 个数<br> Sqld 应用程 序中 使用 的 sqlvar 元素 数<br> Sqlvar 可变的 sqlvar 结构数组 在使用 中可根据输入参数 的个数或结果 集中 数<br>据库列 表的 个数 来分 配数据 空 间<br> Sqltype 代表数据库列表或宿 主变量数据类型 的短整数值 并指出其是否允许 空<br>值 Sqltype 中使用 代码 值来 表示 SQL Server 相应的数 据 类型 表 8-5 说 明了各 个<br>代码值所代表 的数据类型 其中每一对代码值 中 奇 数值表 示该类型的宿 主变 量<br>必须有 一个 指示 变量 来存放 空 值 偶数 值表 示不 允许有 空 值<br> Sqllen 列长 度<br> Sqldata 在 FETCH OPEN EXECUTE 语句中使用 的 宿 主变 量 的 地址 是存放<br>输出 输入 数据 的缓 冲区<br> Sqlind 如果 数据 库列 表允 许空 值 为在 FETCH OPEN EXECUTE 语 句中使 用<br>的指示 变量 的地 址 否 则 不使用 该元 素 如果 数据 库 表列允 许空 值 当数 据为 null<br>时 sqlind 的值为-1 如果 数据 值不 为 null sqlind 的值为 0<br> Sqlname 数据 库表 列的 名字 数据 结构<br> Length 列名的 字节 长度<br> Name 列名<br>在执行 FETCH 语句 之前 应用程 序必 须先 设置 好 接 收相应 数据 库表 列的 数据 的 宿主<br>变量 的地 址 即 sqldata 中必须有 有 效 的宿 主变量地 址 如果 数据库表 列允许空 值 sqlind<br>中也必 须有 有效 的指 示变量 地 址 使用 PREPARE INTO 或 DESCRIBE 语句 可以 从 SQL<br>Server 获得数据库表列的名字 数据类型和长度 并自动填充 SQLDA 数据结构中的<br>sqlname sqltype sqllen 等域 在执 行 FETCH 语句 之前 应 用程 序 还 可以修改 sqltype 和 sqllen<br>值<br>在使用 DESCRIBE 或 PREPARE INTO 语句之 前 必 须设置 sqln 其值应 大于 等 于预<br>期输出 列的 个数 如果 不 知道输 出列 的个 数 可以 将 sqln 设置 为 0 然后执行 DESCRIBE<br>语句 输 出结 果集 中的 数 据库表 列的 个数 将会 填充 到 sqld 中 但数 据库 表列 的 详细信 息不<br>会被填 充到 SQLDA 数 据 结构中<br>如果使 用 SQLDA 数据 结 构输入 数据 应用 程序 必须 设置 SQLDA 数 据结构中的 全 部<br>域的值 包括 sqln sqld sqldabc sqllen sqldata 等域 如果 sqltype 的值为奇数 类 型代码<br>则必须 提供 指示 变量<br>8.4 嵌入式 SQL 数 据 库访 问过程<br> 与 DB-Library 应用程序类 似 嵌入 式 SQL 程序访问 SQL Server 数据 库系 统的 过程 可<br>以分为 以下 几步<br>1) 连接 SQL Server<br>2) 向 SQL Server 发送用 户的操 作命 令<br>3) 处理 SQL Server 返回的命令 结果4) 断开 与 SQL Server 的连接<br>5) 处现 以上 步骤 执行 过程中 产 生的 错误<br>下面简 单介 绍前 四步 的实现 方 法 嵌入 式 SQL 的 错 误 处理将 在下 节介 绍<br>8.4.1 连接 SQL Server<br> 嵌入 式 SQL 应程 序使用 CCONNEC ONNECT TO T TO 语句 建立 与 SQL Server 数 据库的连 接<br>CCONNEC ONNECT TO T TO<br>CONNECT TO CONNECT TO CONNECT TO CONNECT TO 作句 的语 法格式 为<br>CONNECT TO {[server_name.]database_name} [AS connection_name] USER<br>[login[.password] | $integrated]<br>其中 serve_name 为要 登录的 SQL Server 服务 器名 称 database_name 为要连 接的数 据<br>库名 connection_name 为连 接名 称 longin 和 password 说 明登录 SQL Server 所使用 的用<br>户名称 和口 令 $integrated 说明 在登 录 SQL Server 时使 用 Windows NT 集 成安全 认证模 式<br> 如果应用程序 只使 用以个连接 就不 需要 提供 连 接名称 当 使用多个连接时 必须 为<br>每一个连 接指定连接名 称 已命名的 连接在一个进程 中是共 享的 可被程 序中的 各个模 块<br>和动态 链接 库使 用 在 CONNECT TO 语 句 中可 以使用 宿主 变量<br> 下面 例子 说明 CONNECT TO 语句的 用法 这三条 语句 具有 相同 的功 能<br>EXEC SQL CONNECT TO :svr USER :usr;<br>或<br>EXEC SQL CONNECT TO "gizmo.pubs" USER "sa";<br>或<br>EXEC SQL CONNECT TO gizmo.pubs USER sa;<br> 如果 应用 程序 建立 多 个连接 可以 使用 SET CONNECTION 语 句在不同 连接 之 间转 换 SET<br>CONNECTION 语句 的语 法格 式为<br>SET CONNECTI0N connection name<br> 其中 connection name 为己建 立的 连 接 名称 它 可为连 接名 称字 符串 或 包 含 连接名 称<br>字符串 的宿 主变 量 使用 SET CONNECTI0N 语句设 置 了连接 后 其后 的所 有 SQL 语 句 均使用<br>此连接 直到 下一 个 SET CONNECTI0N 语句改 变了 连 接为止 如果 要在 不同 的编译 模块 中使<br>用同一 连接 则必 须使 用 命名连 接 下面 的例 了说 明 多个连 接的 使用<br>EXEC SQL CONNECT TO server1.pubs AS svr1 USER sa;<br>EXEC SQL CONNECT TO server2.pubs AS svr2 USER sa;<br>EXEC SQL SET CONNECTION svr1;<br>EXEC SQL SELECT name FROM sysobjects INTO :name;<br>EXEC SQL SET CONNECTION svr2;<br>EXEC SQL SELECT name FROM sysobjects INTO :name;<br> 第一 个 SELECT 语句 在服 务器 serverl 的 pubs 数 据库上执 行 第二 个 SELECT 语句则<br>在服务 器server2 的pubs 数据 库上 执行<br> GET CONNECTION 语句 为嵌 入式 SQL 程序提 供了 检 索指定 连接 的 DBPROCESS 结 构 指针的<br>方法 其语 法格 式为<br>GET CONNECTION connection_name INTO hvar<br> 其中 connection_name 为一个已打开的连接名称 hvar 为宿主变量 它存储 GET<br>CONNECTION 语句 返回 的 DB-Library 连接 指针 其 数 据类型 为 DBPROCESS<br>8.4.2 命令 处理<br>建立连 接后 嵌入 式 SQL 程序可 向 SQL Server 发送 SQL 命 令进行数 据库 操作 发送 SQL 命<br>令的方 法有 以下 几种<br> 立即执 行方 式 即在 嵌入式 SQL 语句 中直 接使 用静 态 SQL 语句 其中 可包 括单 条<br>Transact-SQL 语句 Transact-SQL 批命 令 数据 库 事务或 存储 过程<br> 准备执 行 方 式 使用 PREPARE EXECUTE EXECUTE IMMEDIATE 语句 执行 动态 SQL<br>语句<br>有关命 令执 行方 式将 在 8.5 节详 细介 绍<br>8.4.3 结果 处理<br>嵌入式 SQL 命令中的 SELECT 语句 或包 含 SELECT 语 句的存储过 程产 生的 结果 集合<br>分为单 结果 行和 多结 果行两 类 相应 的处 理方 法也 不 同<br> 单结果 行 使用 SELECT INTO 语句将结 果行 中的 数 据拷 贝到 宿主 变量 中进 行处 理<br> 多结果 行 使用 游标 处理 FETCH 语句 可以 一次 读 取一行 数据<br> 单结果 行<br>单结果 行的 处理 很简 单 使用 SELECT INTO 命令来 处理 SELECT INTO 命 令 的格式<br>如下<br>SELECT [select_list] INTO {:hvar [,...]} select_options<br>其中 select_list 为查询 语句 的选 择列 表 选择 列表 中 可包括 数据 表的 列名 表达 式等<br>hvar 为存放检索结果数据的宿主变量 宿主变量的个数 数据 类型 长度 顺序 等应 与<br>select_list 选择列表 中 列 或表 达式 的 个 数 数 据类型 长度 和 顺 序相同 如果二者 不匹 配<br>或是数 据被 截断 时 SQLWARN3 的值将 被设 置为 W<br>Select_options 为 Transact-SQL SELECT 语句的 检索 选 项 FROM 子句 或 WHERE 子<br>句 嵌入 式 SQL 不支持 Transact-SQL 中的 GROUP BY HAVING COMPUTE 等子句<br>下面的 例子 说明 SELECT INTO 语句的用 法<br>EXEC SQL SELECT au_lname INTO :name FROM authors WHERE stor_id=:id;<br>如果 SELECT INTO 返 回 的结果 集合 中的 数据 行数 大 于 1 时 则在宿主 变量 中存 放结<br>果集合 中的 第一 行数 据 并将 SQLCODE 的值置 为 1 表明有意 外发 生<br>在 SELECT INTO 中一 般 使用宿 主变 量来 存储 结果 集 合中 的各 列数 据 为了 简单 也 可<br>以使用 C 结构 来存 放结 果集 合数 据 结构 中各 个元 素的 数据 类型 和长 度应 与检索 结 果集 合<br>中各列 相匹 配 如<br>EXEC SQL BEGIN DECLARE SECTION;<br>int id;<br>char name[30];<br>EXEC SQL END DECLARE SECTION;<br> 游标操 作<br>如果嵌 入式 SQL 向 SQL Server 发送检 索命 令 其 返 回结果 是包 含多 行数 据的 结 果集<br>合 要处 理结 果数 据 则 必须定 义和 使用 游标 然后使用 FETCH 语 句逐行处 理 结果集 合中的数 据行 嵌入 式 SQL 游标提供 了一 次处 理多 行数据 的 机制<br>但是 使用 游标 不能 处理 Transact-SQL 批命令或 存储过 程 返回 的多 个结 果集 合 当一<br>个语句返 回多个结果集 合时 只有第 一个结果集合能 够被识 别和处理 其 后面 的 结果集 会<br>将被忽 略 此外 嵌入 式 SQL 游标也不 能处 理 COMPUTE 子 句产生的 统计 数 据行 结果<br>集合中 的 COMPUTE 数 据 行也将 被忽 略<br> 游标的 定义<br>嵌入式 SQL 游标包括 标准游 标 和浏 览游 标两 种类 型 标准游 标是 在程 序中 与别 的 SQL<br>语句共 共享 同一 个到SQL Serve 连接的游 标 使用 SET CURSORTYPE CUR_STANDARD<br>语句或 在游 标定 义语 句 DECLARE CURSOR 中使 用 FOR UPDATE 选 项可以 定义 一个 标准<br>游标 浏览游标则是每个游标均需要一个独立的到 SQL Server 的连接 使用 SET<br>CURSORTYPE CUR_BROWSE 语句可 以定 义一 个浏 览游 标<br>标准游 标和 浏览 游标 的定义 和 使用 方法 是一 致的 标 准游标 允许 多个 游标 共享 一 个 SQL<br>Server 连接 而每 个浏 览游 标则 需要 一个 独立 的 SQL Server 连接 对于 大多 数 应用程 序<br>建议使 用标 准游 标 而 且 对于嵌 入式 SQL 应用程 序 标准游 标是 默认 的 因为 一个 共享 的<br>SQL Server 连接可 以避 免游 标之间 可能 存在 的锁 定冲突 标准 游标 使用 了 DB-Library 的游<br>标函数 因此 使用 标准 游 标的嵌 入式 SQL 应用程 序可以 充 分利 用 DB-Library 游标的增强<br>功能 而且标准游标 同样支持 DB-Library 游 标 的许多 选项 如并发控 制 性 能 特点等<br>由于每 一个 浏览 游标 使用不 同 的 SQL Server 连接 SQL Server 把 每一个 浏览 游 标看作 是一<br>个独立 的用 户 这 可能 在 同一应 用程 序中 的不 同浏 览 游标之 间引 起锁 定冲 突<br> 浏览 游标 在使 用前 必 须先定 义 嵌入 式 SQL 使用 DECLARE CURSOR 语 句定义 游标 其<br>语法格 式如 下<br>DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR FOR<br>{select_stmt | prepared_stmt_name} [FOR { READ ONLY | UPDATE<br>[ OF column_list ] } ]<br>其中 cursor_name 为游 标的名 字 其长 度不 能超 过 30 个字 符 游标 名字 的第 一 个字符<br>必须是 字 母 在游标 名 字中 不能使 用 连 字符 - INSENSITIVE 选 项 指 定 游 标为不敏 感 游<br>标 即为 只读 游标 对 于 只读游 标 在游 标打 开时 SQL Server 为其 建 立一个 结果 集快 照<br>游标 的 所 有操作均在此 快照上 执 行 SCROLL 选项 设 置 游 标 的 滚动属性 它说 明游 标指针<br>可以定 位到 第一 个记 录 最后一 个记 录或 下一 个记 录 select_stmt 为 SELECT 语句 用<br>于定义 静态 游标 结果 集合 浏览 游标 还可 以使 用包 含 SELECT 语 句的存 储过 程 进行定 义<br>prepared_stmt_name 为 PREPARE 语句 准备 的 SELECT 语 句名称 它用 于定 义动 态 游标 FOR<br>READ ONLY 选项定 义一个 标 准的 DB-Library 只读游标 FOR UPDATE 选 项说明通 过游 标<br>可以进 行修 改操 作 该 选 项是默 认设 置<br>下面的 例子 说明 如何 定义标 准 游标 和浏 览游 标<br>//定义 标准 游标c1<br>EXEC SQL DECLARE c 1 CURSOR FOR<br> SELECT au_fname, au_lname FROM authors ;<br>//定义 浏览 游标c2EXEC SQL DECLARE c2 CURSOR FOR<br> SELECT au_fname, au_lname FROM authors FOR BROWSE;<br> 游标的 打开 和关 闭<br>游标被 定义 以后 可 以使用 OPEN 语句打 开该 游标 进行检 索 删除 修改 等操作 操<br>作完成 后使 用 CLOSE 语 句 关闭游 标 释放 游标 所占 用 的资源<br> 游标 打开 语句 的格 式 如下<br>OPEN cursor_name [USING DESCRIPTOR :sqlda | USING :hvar [,...]]<br>其中 cursor_name 为游标的名字 sqlda 为应用程序准备好的用于输入检索参数的<br>SQLDA 数据结构 它包含了每一 个输入参数的地址 数据类型和长度 hvar 为对应于游<br>标 SELECT 语句 中的 参数 标志 符的 宿主 变量<br> OPEN 语句 执行 游标 定义 语句 中的 SELECT 语句 产生游 标结 果集 合<br>对于静 态游 标 其 SELECT 语句可 以包 含宿 主变 量 但不 能包 含参 数标 志符 宿主变<br>量只能用 于常量可以使 用的地方 其 它像表名 列名 数据 库对象或关键 字等地 方不能 使<br>用宿主 变量 当 OPEN 语句 执行 时 宿主 变量 的值 被替 换到 SELECT 语句 中 静态游 标的<br>OPEN 语句 不能 使用 USING hvar 和 USING DESCRIPTOR sqlda 选项<br> 对于动态游标 其 SELECT 语句可以包含参数标 志符 而不能包含宿主变量 如果<br>SELECT 语句中使用了参数标志符 OPEN 语句中必须使用 USING hvar 或 USING<br>DESCRIPTOR Sqlda 选项为 参数 提供 数据 宿主 变 量的个 数或 Sqlda 数 据结构 中的 变员 个<br>数必须 与 SELECT 语句 中的 参数 标志 符的 个数 相对 应<br>浏览游 标使 用独 立的 SQL Server 连接 当打 开一 个浏 览游 标时 如果 试图建 立新 的 SQL<br>Server 连接失败 或打开一个标准游标时没有有效的 SQL Server 连接存在 将 产生一 个<br>SQLCODE 值为-19521 的 运行时 错误<br>游标关 闭语 句的 格式 如下<br>CLOSE cursor_name<br>CLOSE 语句放弃游 标结果 集合中尚未 处理的数据行 并释放游 标打开时所保 持的数<br>据库锁 应用 程序 结束 时 将自动 关闭 所有 打开 的游 标<br>下面的 例子 说明 如何 打开 关闭 游标<br>EXEC SQL DECLARE c1 CURSOR FOR<br> SELECT au_fname,au_lname FROM authors FOR BROWSE;<br>EXEC SQL OPEN c1;<br>while (SQLCODE == 0)<br>{<br> // 如果数据读取成功 SQLCODE 为0<br>EXEC SQL FETCH c1 INTO :fname,:lname;<br>}<br>EXEC SQL CLOSE c1;<br> 检索游 标数 据<br>游标被 定义 和 打 开后 可以使 用 FETCH 语 句一次 读取一 行结果 记录 记录的 值存放在<br>宿主变 量或 SQLDA 数据 结构 指定 的地 址中 FETCH 语 句的格 式如 下FETCH [ [ NEXT | PRIOR | FIRST | LAST ] FROM ] cursor_name<br>[USING DESCRIPTOR :sqlda_struct | INTO :hvar [,...]]<br> 其中 NEXT 指定 读取 游标 结果 集中 的下 一条 记录 如果 第一 次执 行 FETCH 语句 则<br>读取结 果 集 中的第一 条 记录 PRI0R 为 读取游 标 结 果集中的 前 一 条记录 FIRST 将游标指<br>针移到结果集 的第 一条记 录处 并返 回第 一条记录值 LAST 将 游标指针移到结果集的 最<br>后一条 记录 处 并 返回 最 后一条 记录 值 cursor_name 为游 标名称 sqlda_struct 为 存 放游标<br>输出数 据的 SQLDA 数据结 构 hvar 为存 放检 索数 据的 宿主 变量<br>使用 FETCH 语 句前 游标 必 须已定 义和 打开 如果没有 指 定 NEXT PRIOR FIRST<br>LAST 选项 FETCH 语句 自动 采用 NEXT 方法 读取 游标 结果 集中 的下 一条 记录<br>宿主变 量的 数据 类型 和长度 必 须与 游标 SELECT 语句 中的 选择 列表 相匹 配 如 果游标<br>结果 集合 中的 列的 个数 与 宿主变 量的 个数 不匹 配 SQLWARN3 的 值被设 置为 W 如果发<br>生错误 其他 的数 据库 表 列不会 被处 理 当 SQLCODE 的值 为 100 时 指示游标 结果集 中没<br>有更多 的记 录可 被读 取<br>USING DESCRIPTOR sqlda_struct 选项在 静态 游标 和 动态游 标中 均可 使用<br>下面的 例子 说明FETCH 语 句的使 用<br>EXEC SQL DECLARE C1 CURSOR FOR<br> SELECT au_fname, au_lname FROM authors FOR BROWSE;<br>EXEC SQL OPEN C1;<br>while (SQLCODE == 0)<br>{<br> EXEC SQL FETCH C1 INTO :fname, :lname;<br>}<br> 使用游 标修 改数 据<br>在游标中不仅 可以 读取和 处理数据 还可 以对结果集合中的 当前数据行进行删 除和修<br>改操作 与 FETCH 语 句一样 对 游 标的 删除 修改 操 作只能 一次 处理 一行 数据 因此把 游<br>标的删 除 修 改操 作称 为 定位删 除和 定位 修改<br>定位删 除语 句的 格式 如下<br>DELETE [FROM] {table_name | view_name}<br>WHERE CURRENT OF cursor_name<br>嵌入式 SQL 的定位删 除与 Transact-SQL 的删除语句 DELETE 不 同之处在 于 定位 删<br>除只删 除游 标中 最近 由 FETCH 语句访 问的 记录 行 而 Transact-SQL 的删除语 句 DELETE<br>需要搜 索条 件子 句限 定删除 的 数据 行 不提 供 WHERE 子 句时删 除数 据库 表中 的 所有记 录<br>下面的 例子 说明 定位 删除操 作 的使 用<br>EXEC SQL DECLARE c1 CURSOR FOR<br> SELECT au_fname, au_lname FROM authors FOR BROWSE;<br>EXEC SQL OPEN c1;<br>while (SQLCODE == 0)<br>{<br> EXEC SQL FETCH c1 INTO :fname, :lname;<br> if (SQLCODE == 0)<br> { printf("%12s %12s\n", fname, lname);<br> printf("Delete? ");<br> scanf("%c", &reply);<br> if (reply == ''y'')<br> {<br> EXEC SQL DELETE FROM authors WHERE CURRENT OF c1;<br> printf("delete sqlcode= %d\n", SQLCODE(ca));<br> }<br> }<br>}<br>定位修 改与 删除 类似 下 面不再 详细 讲述<br>8.4.4 关闭 连接<br>处理完数据库事务后 应用程序应关闭与 SQLServer 的连接 嵌入式 SQL 使用<br>DISCONNECT 语句 关闭 应 用程序 建立 的数 据库 连接 DISCONNECT 语 句的语 法格式 为<br>DISCONNECT [connection_name | ALL | CURRENT]<br>嵌入式 SQL 关闭一个 连接时 该连 接下 建立 的所 有游标 均 自动 被关 闭 为了 保证 在退<br>出时释 放程 序所 占用 的资源 嵌入 式 SQL 程 序 在退 出 主程序 之前 应调 用 DISCONNECT ALL<br>语句<br>8.4.5 错误 处理<br>嵌入式 SQL 提供 SQLCODE 变量 SQL 通信区 SQLCA 结 构中的一 个域 记录 SQL<br>语句的 执行 状态 每一 条 SQL 语句 执行 之后 应用 程 序可检 查 SQLCODE 变量的 值 如果<br>其值为 0 表明 SQL 语句执行 成功 否则 说明 语句 执 行时发 生错 误 表 8-6 列出了 SQLCODE<br>变量值 的意 义<br>表 8-6 SQLCODE 变量值的意义<br>条件 SQLCODE 变量取值 意义<br>SUCCESS 0 执行正确 没有错误发生<br>SELECT 语句或游标结果集中数据已取完或没有返回数据<br>NOT FOUND 100<br>SQLWARNING +1 发生嵌入式 SQL 警告或错误 如输出数据被截断<br>SQL Server message<br>SQLERROR <0 发生 错误<br>8.5 SQL 命令执行方 式<br> 嵌入 式 SQL 支持静态 SQL 语句 和动 态 SQL 语句 两种 SQL 语 句有不同 的 执行方 法<br>对于静 态 SQL 语句 可 使用立 即执 行方 式 对于 动态 SQL 语句 则使 用准 备执 行方 式<br>即使用 PREPARE EXECUTE 和 EXECUTE IMMEDIATE 语 句处理<br>8.5.l 立即 方式 执行 SQL 命令<br> 立即方式执行 SQL 命令用于静态 SQL 语句 在嵌入式 SQL 源程序中直接使用<br>Transact-SQL 命令 包括 单条 Transact-SQL 语句 批命 令 事务 或存 储过 程等 立即方 式执行 SQL 命令可 通过 宿主变 量 来输 入 输出 数据 它 适用于 数据 库结 构明 确或 将来 改动 不<br>大的环 境<br> 下面 的例 子说 明立 即 方式执 行 SQL 命令 它检 索 authors 表 并将 检索 结果 存入 宿主<br>变量<br>EXEC SQL SELECT au_lname,au_fname INTO : lname, : fname<br>FROM authors WHERE au_id = : id;<br>该嵌入 式 SQL 语句 向 SQL Server 发送单条 Transact-SQL 检索命 令 并将 检索 结果 存<br>储到宿 主变 量 lname 和 fname 中 下 面的例 子以 立即执 行 方式 向 SQL SCfVef 数 据 库中插<br>入一条 记录<br>EXEC SQL INSERT authors au_id au_lname au_fname contract<br>VALUES ”172-32-1176” ”White” ”Johnson” 1 ;<br>8.5.2 修改 数据<br> 修改 数据 包括 记录 的 插入 修改 和删 除等 操作 插入记 录使 用 INSERT 语 句 来完成 修<br>改和删除 记录操作因处 理条件的不同 可分为检索修改 删除 和定位修改 删除 两种<br>定位修改 删除 主要 在游标中使用 这方面内容将 在游标 操作一节中详 细介绍 本节 主<br>要介绍 检索 修改 删除 操作<br> 检索 修改 语句 的格 式 为<br>UPDATE {table_name | view_name}<br>SET [table_name. | view_name.] {column_name=<br>{expression | NULL | (select_statement)}[,...]}<br>[FROM {table_name | view_name}[,...]]<br>[WHERE search_condition]<br>其中 table_name 或 view_name 为要处理 的表 或视 图名称 column_name 为要 修改 的列<br>名 expression 为 C 表达 式 select_statement 为只返回 1 条 记录且只 包含 单列 的 SELECT<br>语句 search_condition 为 搜索条 件 它指 定要 修改 的 数据行<br>下面的 例子 说明 UPDATE 语句的 使用<br> EXEC SQL UPDATE authors SET au_fname=’Fred’<br> WHERE an_lname ’White’<br> 检索 删除 语句 的格 式 为<br> DELETE [FROM table name| viewname<br> WHERE search_conditions<br> 其中 table_name 或 view name 为要处理 的表 或视 图名 称 search_condition 为搜索 条<br>件 它指 出待 删除 的数 据 行<br>DELETE 是标 准的 Transact-SQL 语句 它删 除数 据表 中满 足 WHERE 条 件的记 录 如<br>果不使 用 WHEREf 句提 供 搜索条 件时 则删 除指 定表中 的 所有 记录 删除 了全 部记 录后 空<br>表仍然 存在 直到 使用 DROP TABLE 语句 删除 表为止<br>如果 DELETE 语句 所处 理 的视图 中包 含多 个数 据表 则不能 执行 DELETE 操作 因为<br>嵌入式 SQL 不支持对 多基表 视 图的 删除 操作 同样 UPDATE 语句 和 INSERT 语句也只 能 对<br>单基表 现图 操作<br>下面例 子说 明 DELETE 语 句的用 法 EXEC SQL DELETE FROM authors WHERE au_lname ’White’<br> 立即执行方式的修改和删除操作适用于搜索条件和修改值已知的情况下使用静态<br>SQL 语句 来处 理 如果 需 要用户 动态 地输 入搜 索条 件 或修改 值 则应 使用 动态 SQL 来处理<br>8.5.3 执行 SQL 批命 令和 事务<br>立即方 式执 行 SQL 命 令 不 仅可以 执行 单条 Transact-SQL 命令 同样 可以 执行 Transact-<br>SQL 批命令 和事 务<br>例如 下面 嵌入 式 SQL 语句执 行一 个 Transact-SQL 批命令 它向 SQL Server 数据库<br>插入两 条记 录<br>EXEC SQL<br>INSERT INTO authors VALUES ‘111-22-3333’,’Tom’,’Fozzy’,<br>‘123-444-5678’,’Mmuppet Show’,’Wall Street’,’wa’,’10002’,1<br>INSERT INTO authors VALUES ‘222-33-4444’,’Henry’,’Kermit’,<br>‘123-444-5678’,’Seame’,’Wall Street’,’wa’,’10002’,1<br>GO;<br>事务是 SQL Server 的单个工 作单 元 一个 事务 内的 所有 Transact-SQL 语句被 作为 整体<br>执行 下面 的例 子说 明事务 的使 用 方法<br>EXEC SQL<br>BEGIN TRANSACTION<br>INSERT INTO authors VALUES ‘111-22-3333’,’Tom’,’Fozzy’,<br>‘123-444-5678’,’Mmuppet Show’,’Wall Street’,’wa’,’10002’,1<br>INSERT INTO authors VALUES ‘222-33-4444’,’Henry’,’Kermit’,<br>‘123-444-5678’,’Seame’,’Wall Street’,’wa’,’10002’,1<br>COMMIT TRANSACTION<br>下面的 例子 说明 了数 据库回 滚 事务 操作<br>EXEC SQL<br>BEGIN TRANSACTION rollback_authors<br>INSERT INTO authors VALUES ‘111-22-3333’,’Tom’,’Fozzy’,<br>‘123-444-5678’,’Mmuppet Show’,’Wall Street’,’wa’,’10002’,1<br>INSERT INTO authors VALUES ‘222-33-4444’,’Henry’,’Kermit’,<br>‘123-444-5678’,’Seame’,’Wall Street’,’wa’,’10002’,1<br>ROLLBACK TRANSACTION rollback_authors<br>在示例 中 首先 向 authors 表插 入 2 条记录 然后 回 滚该事 务 authors 表恢 复到 事<br>务执行 前的 状态<br>8.5.4 执行 存储 过程<br>嵌入式 SQL 支持 SQL Server 存储过程 的调 用 存储过 程 可接受 参数 并返 回状 态值<br>和参数 值 下 面的 例子 说 明存储 过程 的执 行方 法<br>EXEC SQL exec master.sp_addtype demo_new_type,int;<br>IF (SQLCODE = = 0){<br>EXEC SQL SELECT name into :added_type FROM master.systype<br>WHERE name =”demo_new_type”;<br>IF (SQLCODE = = 0 | |SQLCODE = = 1) {<br>Printf(“%s\n”,added_type );EXEC SQL exec master.sp_droptype demo_new_type;<br>}<br>}<br>在该 例中 首先执行 SQL Server 系统存储过程 sp_addtype 定义一个新的数据类型<br>demo_new_type 然后使用 SELECT 检索语句检 索该 数据 类型 如果存 在显 示新 数据 类型<br>的名称 调用 sp_droptype 存储过 程删 除它<br>8.5.5 准备 方式 执行 SQL 命令<br>对于动 态 SQL 语句 嵌入式 SQL 提供 了准 备方 式来 执行 应用 程序 运行 时生 成的 动态<br>SQL 语句 准备 方式 执行 SQL 命令 时 首先 需调 用 PREPARE 语 句准备 要执 行 的动态 SQL<br>语句 然后使用 EXECUTE 语句来执行准备好的动态 SQL 语句 或使用 EXECUTE<br>IMMEDIATE 语句 直接 执行 动态 SQL 语句 下面 分 别介绍 执行 动态 SQL 语句要 用到 的几<br>个嵌入 式 SQL 命令<br> PREPARE 语句 的格 式 为<br>PREPARE stmt_name [INTO :sqlda] FROM :hvar<br> 其中 stmt_name 是 将 要执行 的动 态 SQL 语 句 的名称 hvar 是包 含 SQL 语 句 的字符串 型<br>宿主变 量 sqlda 是用 来 输出数 据的 SQLDA 数据结 构<br>PREPARE 语 句从 字 符串 型 宿主变 量中 接受 动态 SQL 语句 并给 该语 句赋 一个 名字<br>便于以 后执 行 PREPARE 语句定 义的 语句 名在 一个 程序 模块 中是 全局 性的 一个 程序 模块<br>不能用 多个 PREPARE 语 句定义 同一 个语 句名 语句名 也 不能在 不同 的程 序模 块中 共享<br> 如果 使用 EXECUTE 语 句执行 动态 SQL 语句 则 宿 主变量 中包 含的 SQL 语句 不能 返 结果<br>动态 SQL 语句 要返 回结 果 只能 通过 定义 动态 游标 来 实现<br>PREPARE 语句 准备的 动态 SQL 语句 中不 能包 含宿 主变 量或 注释 但可 包含 参数 标志<br>符 需要 输入 参数 的地 方用 代替 动态 SQL 语句 中 也 不能包 含嵌 入式 SQL 的<br>保留字<br>EXECUTE 语句 的格 式为<br>EXECUTE prepared_stmt_name [USING DESCRIPTOR :sqlda_struct |<br>USING :hvar [,...]]<br> 其中 prepared_stmt_name 是 PREPARE 语句 定义 的动 态 SQL 语句 名 hvar 是 一 个或多<br>个用来 输入 数据 的宿 主变量 sqlda_struct 是已 定义 并 包含输 入数 据的 SQLDA 数据结 构<br>EXECUTE 语句 执行 已 准 备好的 动态 SQL 语句 使用 宿主 变量 或 SQLDA 数据结 构 输<br>入数据 如果 动态 SQL 语句中 包含 参数 标志 符 EXECUTE 语 句必须使 用 USING<br>hvar 选项或 使用 USING DESCRIPTOR sqlda_struct 选 项提供 参数 参数 的个 数 数据 类<br>型和顺 序必 须与 动态 SQL 语句中定 义的 相同 EXECUTE 语 句不支持 有返 回结 果的 动态 SQL<br>语句<br>下面的 例子 说明 如何 使用 PREPARE 和 EXECUTE 语 句执行 动态 SQL 语句<br> EXEC SQL BEGIN DECLARE SECTION;<br> char prep[] = "INSERT INTO mf_table VALUES(?,?,?)";<br>char name[30];<br>char car[30];<br>double num;<br>EXEC SQL END DECLARE SECTION;<br>EXEC SQL PREPARE FROM :prep;<br>while (SQLCODE == 0)<br>{<br> strcpy(name, "Elaine");<br> strcpy(car, "Lamborghini");<br> num = 4.9;<br> EXEC SQL EXECUTE prep_stat USING :name, :car, :num;<br>}<br> 在该 例中 由 PREPARE 语句 定义 了一 个动 态 SQL 语句 prep_stat 该动态 SQL 语句完<br>成向 SQL Server 数据 库 authors 表中插 入一 条记 录 的操作 然后 使用 strcpy 函 数 为宿主<br>变量赋 值 最后 用 EXECUTE 语句 执行 准备 好的 动态 SQL 语句 并提供 插入 语句 所 需的 3 个<br>参数值<br>8.6 嵌入式 SQL 选项 设置<br> 嵌入 式 SQL 使用 SET OPTION 语句设 置 SQL Server 的 查询处理 选项 SET OPTION 语<br>句的语 法格 式为<br>SET OPTION {QUERYTIME | LOGINTIME | APPLICATION | HOST} value<br> 其中 各选 项的 意义 如 下<br> QUERYTIME 设 置命 令 响应超 时时 限 即执 行 Transact-SQL 语句后 嵌入 式 SQL<br>应用程 序等 待 SQL Server 对该语 句响 应的 时间 以秒 为单 位 默认 值为 0 表明<br>等待时 间没 有限 制 设置 QUERYTIME 选项 与 DB-Library 中的函数 dbsettime 功<br>能相同<br> LOGINTIME 设置 登录 超时 时限 即应 用程 序执 行 CONNECT TO 语 句等录 SQL<br>Server 服务器 时 嵌入 式 SQL 应用程 序等 待 SQL Server 响应的时 间 单位 为秒<br>默认值 为 10 秒 LOGINTIME 选项 的作 用与 DB-Library 中的函数 dbsetlogintime<br>的功能 相同<br> APPLICATION 指出 应用 程序 名称 它设 置 DB-Library LOGINREC 结 构中的应<br>用程序 名称 值 设置 APPLICATION 与 DB-Library 中 dbsetapp 函 数的功能 相同<br> HOST 指出用 户主 机名 称 它设 置 DB-Library LOGINREC 结 构中的 用户 主机 名<br>称值 HOST 选项 的作 用与 DB-Library 中 dbsethost 函 数 的 功能相同<br> 嵌入 式 SQL 不支持其它 的 DB-Library 选项和 SQL Server 选项设 置 但 SQL Server<br>选项可 通过 Transact-SQL 语句进行 设置<br>8.7 建立嵌 入式SQL 应用程 序<br> 如果 要开 发某 一环 境 如 Windows 2000 Windows NT 或 Windows 95 下的 应用程 序<br>必须在 此应 用环 境下 编译 链接 嵌入 式 SQL 源程序 本节将 详细 介绍 嵌入 式 SQL 应用程 序<br>的建立 步骤 和运 行方 式 并给出 一个 例子 说明 如何 编 译 链接 嵌入 式 SQL 程序<br>8.7.l 应用 程序 建立 步骤<br>所有的 嵌入 式 SQL 应 用 程 序都应 按以 下步 骤来 建立<br> 使用 C 集成 开发 环境 或文 本编 辑器 建立 嵌入 式 SQL 源程 序 所有 的嵌 入式 SQL<br>源程序 均以.sqc 作文 件扩展 名<br> 运行相 应的 预编 译器 sqlprep.exe(Windows 3.x 或 MS-DOS 环境 下) 或 nsqlprep.exe<br>Windows NT Windows95/98 等 32 位操 作系 统 将 嵌入式 SQL 源 程序转 换为 C<br>源程序<br> 运行 C 编译 器将 嵌入 式 SQL 预编译器 产生 的 C 源程序编 译为 目标 文件<br> 运行链 接程 序将 目标 文件 嵌入 式 SQL 库文 件及 其它需 要 链接 的库 文件 链接起来<br>生成可 执行 文件<br>8.7.2 应用 程序 运行 方式<br>当运行 一个 嵌入 式 SQL 应用程 序时 程序 中的 SQL 语句按 如下 方式 执行<br> 对于所 有的 SQL 语句 应 用程序 调用 嵌入 式 SQL 运行 时动 态链 接库<br> 如果 SQL 语句是 静态 的 嵌入式 SQL 运行库 将使 用给 定的 参数 执行 已编 译好 的<br>存储过 程 如果 SQL 语句是动 态的 嵌入 式 SQL 运行 库直 接将 动态 生成 的 SQL<br>语句发 送到 SQL Server 执行<br> 嵌入式 SQL 运行库调 用 DB-Library 函数向 SQL Server 发送 SQL 命令<br> 运行库 将检 索到 的数 据填入 C 宿主变量 或 SQLDA 数 据结构 中 执计 状态 和<br>错误信 息填 入 SQLCA 数据 结构 中<br>图8-1 嵌入式 SQL 应用程序建立 运行过程<br>图 8-l 说明了嵌 人式 SQL 应用程序 的编 译 运行 过程 以及 各个 过程 中所 使用 的 输入<br>库和运 行库 动态 链接 库<br> 嵌入 式 SQL 支持所有的 Transact-SQL 扩展性能 包括 存储 过程 局 部变 量 以及流 控制<br>语句 为了 避免 语法 冲突 使用 Transact-SQL 扩展性能 需遵 循如 下限 制<br> 将 Transact-SQL 的 EXECUTE 语句缩 写为 EXEC 以避免与 嵌入 式 SQL 的 EXECUTE<br>语句冲 突<br> 避免 在静 态 SQL 语句中使 用 Transact-SQL 语句标号 因为 它已 与宿 主变 量 的语法 产生<br>冲突 但在 动态 SQL 语句中可 以使 用 Transact-SQL 语句标号<br>因为嵌 入式 SQL 预编译器将 静 态 SQL 语句转 换到 存储 过程 所以 问 SQL Server 对存<br>储过程的限制同样适用于静态 SQL 语句 包含单个事务管理语句 如 COMMIT 或<br>SAVPOINT 的静 态 SQL 语句不 被编 译为 存储 过程 而是 在运 行时 被动 态执 行<br>8.7.3 使用 预编 译器<br> 嵌入 式 SQL 预编 译器 sqlprep.exe 或 nsqlprep.exe 在源程序 中查 找并 分 析 SQL 语<br>句 然后 建立 能被 相应 的 C 编译器编 译的 C 源程序 在使用 嵌入 式 SQL 预 编译器 之前 需<br>要做如 下准 备工 作<br> 如果 所使 用的 C 编 译 器不是 cl.exe 或 C 编译程 序不在 系统 环境 变量 PATH 指定的 搜<br>索路径 中时 必须 使用 COMPILER 环境变 量指 出编 译 器的名 称和 存储 路径 如<br> SET COMPILER=D \mybin \cl.exe<br> 设置 INCLUDE 环 境变量 使其 指向 存储 嵌入 式 SQL 头文 件 Sqlca.h 和 Sqlda.h 的目录<br>路径 如<br> SEET INCLUDE D \PROGRAM FILES\MICROSOFT SQL SERVER DEVT00LS INCLUDE<br> C \MSDEV INCLUDE<br> 嵌入 式SQL 预编 译器使 用C 编译器来 处理 头文 件 并自 动将 这些 头文 件包 含到C 源程<br>SQL<br>序中 所以 不 需要 在嵌入 式 源程序 中包 含这 些 头文件<br> 在编 译时 如果 使用/DB 和/PASS 选项 为了 能够连 接 到SQL Server 要 确保相应的<br>Net-Library 文件 已被装 载<br> 如果 使用C 集成 开发 环境 以上 的路 径设 置可 先在C 集 成环境的选 项菜 单 中设置<br> 嵌入 式 SQL 预 编译 器 的语法 格式 如下<br> sq 1prep [/SQLACCESS | /NOSQLACCESS<br>[/FLAGGER {ENTRY|NONE}] [/DB [server_name.]database_name<br> /PASS login[.password]$INTEGRATED ][/BIND file_name]<br> [/MSG file_name] [/NOLOGO] [/PLAN name][/NOLINES]<br> [/user_defined_option]<br> 各编 译选 项的 作用 为<br> /SQLACCESS 指 定预 编 译器自 动为 静态 SQL 语 句 建 立存储 过程 同时 必须 使 用<br>/DB 和/PASS 选项 来连 接 SQL Seryer 或使用 FIND 选 项建立 一个 绑定 文 件<br>/NOSQLACCESS /DB<br> 指定预编译器不自动建立存储过程 如果 同时 使用 了 和<br>/PASS 选项 预编 译器 将提 示一 个信 息 然后 连接 到 SQL Server 并删 除以 前建 立<br>的存储 过程<br> /FLAGGER 指定 预编 译 器在编 译阶 段将 静态 SQL 语句发 送到 SQL Server 进行语<br>法检查 SQL Server 将显 示 SQL 语句 编 译 过 程 中 所发 现的语 法 和编译错误 使<br>用/FLAGGER 选项 时 必 须同时 使用/DB 和/PASS 选项 但 FLAGGER 选 项不能<br>与/PLAN 或/SOLACCESSA 选项同 时使 用<br>其中 ENTRY 参数 说明 静态 SQL 语句 必须 同时 遵循 FIPS 127-2 标准 定义 的 SQL 一致<br>性要求 而 NONE 参数 则 说明只 对静 态 SQL 语句 进行 语法 检查 而 不要 求它符合 FIPS 127-2<br>标准定 义<br> /DB 指定 要连 接并 存放 存 储过程 的 SQL Server 服务器 和数 据库 名 名称 必须 与<br>源程序 中 CONNECT TO 语句使 用的 名称 相同 如果 SQL Server 是 运行在 本地 计<br>算机上 可不 指定 SQL Server 名 当使 用/DB 选项时 必须 同时 使用/PASS 选项<br> /PASS 指定 连接 SQL Server 时使用 的用 户名 和口 令 其中$INTEGRATED 指出<br>使用 Windows NT 集成 安 全模式 登录<br> /BIND 指定 预编 译器 产生 的绑 定文 件名 文件 名称 后缀.bnd 被 自动添 加<br> /PLAN 指定 使用 非默 认 的访问 计划 名 默认 的访 问 计划名 与嵌 入式 SQL 源程序<br>名相同<br> /MSG 指定 记录 预编 译 器 产生的 警告 和错 误信 息的 文件 名称<br> /user_defined_option 指定 将传 递给 C 编译器的 用户 定义 选项<br>在预编 译时 如果 使用 了/DB 和/PASS 选项 预编 译器 将连 接到 SQL Server 为每 一<br>个独立 的程 序模 块建 立一个 访 问计 划 访 问计 划包 含 在编译 过程 中为 每一 个静 态 SQL 语句<br>所产生 的存 储过 程 默 认 情况下 存储 过程 名由 以下几 部 分组 成<br> 程序模 块名<br> 8 个可 打印 字符 的日 期/ 时间 标志<br> 一个美 元符 号 $<br> 访问计 划编 号<br>如果编 译时 要连 接的 SQL Server 无效或 不需 要连 接 SQL Server 时 可使 用/BIND 选<br>项建立 一个 绑定 文件 绑定 文 件是 一个 为访 问计 划建立存储过程的 Transact-SQL 批命令 文<br>件 用户 可以 在晚 些时 候 使用 SQL Server 的 ISQL 工 具运行 绑定 文件 在数 据库上 建 立相<br>应的存 储过 程 要 注意 的 是在运 行应 用程 序之 前必 须 先运行 绑定 文件<br>8.7.4 编译 链接 及调 试<br>下面举 一个 简单 的例 子说明 嵌 入式 SQL 应用 程序 的编译 链接 过程<br>//FILENAME :sqldemo.sqc<br>viod error_handler (void)<br>#include <stddef.h><br>#include <stdio.h><br>int main()<br>{<br>// 定义宿主变量<br>EXEC SQL BEGIN DECLARE SECTION;<br>Char id[] = “111-22-3333”;<br>Char lname[40];<br>Char fname[20];<br>EXEC SQL END DECLARE SECTION;<br>// 错误处理函数<br>EXEC SQL WHENEVER SQLERROR CALL error_handler();<br>// 选项设置<br>EXEC SQL SET OPTION LOGINTIME 10;<br>EXEC SQL SET OPTION QUERYTIME 60;<br>// 连接SQL Server<br>EXEC SQL CONNECT TO WebServer.pubs USER sa;<br>If (SQLCODE= =0)<br>Printf (“SQL Server 连接已经建立\n”);<br>Else<br>{<br>printf (“ 错误 SQL Server 连接失败\n”);<br>return (1);<br>}<br>// 使用静态SQL 语句 发送检索命令<br>EXEC SQL SELECT au_lname,au_fname INTO :lname,:fname<br>FROM authors WHERE au_id =:id;<br>// 显示检索结果<br>printf (“ 作者 %s %s\n”,lname,fname);<br>// 关闭连接<br>EXEC SQL DISCONNECT ALL;<br>Return (0);<br>}<br>// 定义错误处理函数<br>void errror_handler (void)<br>{<br>printf (“ 错误处理函数被调用 \n”);<br>printf (“ SQL Code = %l \n”,SQLCODE);<br>printf (“ SQL Server 信息 %l %Fs’\n”,SQLERRD1,SQLERRMC);<br>}<br>对于嵌 入式 SQL 源程序 首先使 用 sqlprep.exe 预编 译器 生成 对应 的 C 源程 序 然后<br>使用 C 编译 器将 C 源程 序 编译成.obj 目标文 件 最 后使 用链 接程 序将所 有目 标模 块和 链接<br>库链接 成执 行文 件 使 用 链接程 序时 要指 明链 接库 文 件的详 细路 径 可设 置 LIB 环境变 量<br>来指定 如<br>SEET LIB D \PROGRAM FILES\MICROSOFT SQL SERVER DEVT00LS LIB<br> C \MSDEV LIB<br>链接嵌 入式SQL 应 用程 序 时有以 下几 个链 接库 必须 链 接<br>32 位 Windows 环境 caw32.lib 和 sqlakw32.lib<br> 16 位 Windows 环境 caw.lib 和 sqlakw.lib<br> MS-DOS 环境 car.lib rldb.lib 和 sqlakd.lib<br>如果在 编译 和链 接时 设置相 应 的调 试选 项 嵌入 式 SQL 应 用程序 可使 用 Microsoft 的调试程 序或 其兼 容的 调试程 序 进行 源程 序级 调试 并 可在嵌 入式 SQL 语 句上设 置断 点对 宿<br>主变量 进行 监控<br>按以上 方法 将 sqldemo.sqc 编译 链接 后 生成 明 sqldemo.exe 文件 其 执行 结果如 下<br>SQL Server 连接已建立<br>作者 White<br>8.8 小 结<br>本章介 绍了 一些 嵌入 式 SQL 语句的 程序 开发 方法 具 体讲了 嵌入 式 SQL 的 程序开发<br>环境 数 据类型及语法 数据库访问 过程和选项设置 等内容 最后以一个 实例结 尾 使读<br>者对整个 过程有清晰的 印象 这种数 据库编程方法的 优点在 于可移植好 只要 有 预编译 器<br>和 C 编译 器 就能 运行 程序 对相 关构 件的 支持 要求 不高</td> </tr> </tbody> </table> <div id="viewerPlaceHolder" style="width: 717px; height: 700px; display: none;"> </div> </span> <table> <tbody> <tr> <td></td> </tr> </tbody> </table> </td> </tr> <tr> <td> <div class="left list" style="text-align: left; font-size: 14px;"> <div id='lastart'></div> <div id='nextart'></div> </div> </td> </tr> <tr> <td style="height: 29px;"> <div style="padding-top: 20px;"> <table style="width: 100%;" cellpadding="0" cellspacing="0"> <tbody> <tr> <td width="31%"></td> <td> <table cellpadding="0" cellspacing="0" style="margin: 0px auto;"> <tbody> <tr> <td><div class=" left" style='cursor: pointer'> <a href="javascript:void(0);" onclick="wzhit(22);SaveArt();"> <img src="http://pubimage.360doc.com/wz/tb11.gif" /></a> </div> <div class=" left xianhua" id="sendFlowerToUser" style='cursor: pointer' onclick='Showflowerlayer("sendedLayer1");'> 献花(<span id="articleflowernum">0</span>)<div id='flowernumadd' class="addtionone">+1</div> </div> </td> </tr> </tbody> </table> </td> <td width="31%" style="vertical-align: bottom; font-size: 12px;"> <div class="right" id="sharediv2"> </div> </td> </tr> </tbody> </table> </div> </td> </tr> <tr> <td> <div class="xiantao" style="padding-top: 5px;"> </div> </td> </tr> <tr> <td class="xgiambox" style="vertical-align: top; height: 30px; padding-top: 10px;"> <div class="right">(本文系<span class="name"><a target="_blank" href="http://www.360doc.com/userhome/70444480">shouhuyanya...</a></span>原创<span id="docsource"></span>)</div> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> </td> </tr> </tbody> </table> <div class="lswzbox"> <div> <table cellpadding="0" cellspacing="0"> <tbody> <tr> <td> <div class="bottom_article f_left"> <div class="str_border"> <strong>类似文章</strong> <a href='http://www.360doc.com/relevant/1053049317_more.shtml' target='_blank' class='a_more f_right' onclick='artStatistics("20-9-8");'>更多</a> </div> <ul class="barticle_list"> <li><span><a href=http://www.360doc.com/content/09/0527/16/144699_3675107.shtml target=_blank onclick='artStatistics("20-9-7");'>sql server 2005学习笔记之触发器简介 - 一个世界一个家,我爱中国! - C...</a></span></li> <li><span><a href=http://www.360doc.com/content/11/0109/15/3554006_85223765.shtml target=_blank onclick='artStatistics("20-9-7");'>PowerDesigner中如何生成主键和自增列--SQL SERVER版本 - NetS...</a></span></li> <li><span><a href=http://www.360doc.com/content/09/0922/13/267101_6287966.shtml target=_blank onclick='artStatistics("20-9-7");'>数据库创建,设置,修改</a></span></li> <li><span><a href=http://www.360doc.com/content/15/0312/15/21464170_454589152.shtml target=_blank onclick='artStatistics("20-9-7");'>触发器(Trigger)(八)</a></span></li> <li><span><a href=http://www.360doc.com/content/06/1117/16/548_263316.shtml target=_blank onclick='artStatistics("20-9-7");'>如何在ASP中连接MySQL数据库</a></span></li> <li><span><a href=http://www.360doc.com/content/09/0819/13/163747_5047357.shtml target=_blank onclick='artStatistics("20-9-7");'>SQL Server 连接'sa'登录失败解决方案</a></span></li> <li><span><a href=http://www.360doc.com/content/18/0121/17/30583536_723908179.shtml target=_blank onclick='artStatistics("20-9-7");'>SQL 简介</a></span></li> <li><span><a href=http://www.360doc.com/content/11/0505/21/2097544_114662092.shtml target=_blank onclick='artStatistics("20-9-7");'>SQL Server 2008中的代码安全(二):DDL触发器与登录触发器 - 邀月工作室 - 博客园</a></span></li> <li><span><a href=http://www.360doc.com/content/15/1129/21/8896884_516794540.shtml target=_blank onclick='artStatistics("20-9-7");'>触发器(trigger)</a></span></li> </ul> </div> </td> </tr> </tbody> </table> <div id="Reflction"> <div id="360docRefTN"> </div> <div id="360docRefCT"> </div> <div id="360docRefPB" align="center"> </div> </div> <a name="sf"></a> <div id="ReflectionPart"> <div class="mainbottom"> <div class=" left"> <img src="http://pubimage.360doc.com/wz/tb22.gif" /> </div> <div class="plbox left"> <div class="plmain"> <div class="titwx">发表评论:</div> <div> <textarea name="SendRefTB" class="pltxt" id="SendRefTB" onfocus="testContent(1);" onblur="testContent(2)"></textarea> </div> <div class="right"> <img id="ImgSendPL" src="http://pubimage.360doc.com/wz/tb24.gif" style="cursor: pointer;" onclick="SubmitReflection()" width="75" height="28" /> </div> </div> </div> <div class=" left"> <img src="http://pubimage.360doc.com/wz/tb23.gif" /> </div> </div> </div> </div> </div> </td> <td align="center" valign="top" width="18px"></td> <td align="left" valign="top" width="252px"></td> </tr> </tbody> </table> </div> <script language="javascript" type="text/javascript"> var appName = navigator.appName.toLowerCase(); if (appName.indexOf("microsoft internet explorer") > -1) { document.write("<scr" + "ipt type='text/javascript' src='http://www.360doc.com/js/StickySystemIENewVersion.js?t=2012122401'></sc" + "ript>"); } else { document.write("<scr" + "ipt type='text/javascript' src='http://www.360doc.com/js/StickySystemOtherNewVersion.js?t=2012122401'></sc" + "ript>"); } </script> <span id="LayerLogin"></span> <script>GerLookingUserInfo(1,70444480,1,56,'-1',-1,10);OutputSource('http://www.360doc.com/userhome/70444480');OutputCategory(70444480,1,'馆藏分类');</script> <script src="http://blockart.360doc.com/ajax/getstatusv2.ashx?aid=1053049317" type="text/javascript" charset="utf-8" async="async"></script> <script type="application/ld+json"> { "@context": "https://ziyuan.baidu.com/contexts/cambrian.jsonld", "@id": "http://www.360doc.com/document/22/1024/09/70444480_1053049317.shtml", "title": "SQLServer基础学习教程总结", "pubDate": "2022-10-24T09:21:08" } </script> </body> </html>