欢迎进入 Office Space 专栏,本专栏提供 Microsoft(R) Office 应用程序脚本编写的提示和技巧。我们在每周四发布新的提示信息;要查看以前发布的提示信息的存档,请访问 Office Space Archive
。如果您遇到了有关 Microsoft Office 脚本编写的具体问题,请尽管将它们发送到 scripter@microsoft.com。我们虽然不能保证回答收到的所有问题,但会竭尽全力。
从 Microsoft Access 数据库中读取数据
您知道,每个家庭都有一个有点特别的家庭成员。提醒您,是好是坏都不重要,只是特别。也许是棕发家族中的红头发成员,或者是开烤肉馆家族中的素食主义者。也许是来自酷爱读书的家庭的体育明星,或者是一个工作相当努力的脚本专家。不管怎样,总有那么一个家庭成员,人们看到就会说,“那是您妹妹?我从来没想到是您妹妹”。
在 Microsoft Office 家族中,这个成员肯定非 Microsoft Access 莫属。对于其他 Office 应用程序(尤其是 Word 和 Excel),您可以使用脚本以编程方式向文档中添加数据;唯一的缺点就是必须使用专用对象模型和专用方法。这并不意味着很难,只是表示对于 Word 和 Excel 您必须学习两个对象模型:知道如何向 Word 文档中添加数据并不能在向 Excel 电子表格中添加数据时帮上多大忙。
然而,Microsoft Access 就不同了。Access 拥有自己的对象模型,但涉及到在表格中读取和写入数据这样的基本数据库操作时,一般情况下不需要使用该对象模型。相反,您要依赖 ADO:ActiveX 数据对象。它确实是一个功能强大的插件。为什么呢?嗯,是因为 ADO 是一个可与任意数量的数据库类型配合使用的标准的编程模型。用于 SQL Server 数据库行不行?没问题:用于 Access 的代码在 SQL Server 中也可以使用,很可能无需做任何调整。用于 Oracle 数据库呢?同样没问题:标准的 ADO 代码也可以用来访问 Oracle 数据库。Visual FoxPro 呢?DB2 呢?AS 400 呢?MySQL 呢?对,您猜对了:在这些数据库中同样可以使用 ADO,我们只是举了几个例子。
了解这些以后,我们认为应该花些时间向您介绍 ADO 了,特别要向您展示如何在 Access 数据库中使用 ADO。今天我们将从基础知识开始 - 连接 Access 数据库和检索信息,然后继续其他任务,例如,添加新记录、修改现有记录、创建表和删除表,以及其他任何我们能想到的操作。
|
注意:本文假定您已有(或可以轻松创建)一个名为 C:\Scripts\Inventory.mdb 的数据库;我们还假定该数据库包含一个名为 GeneralProperties 的表并且该表包含一个名为 ComputerName 的字段。如果您不理解我们提到的像“表”和“字段”这样的术语的话,那本文很可能对您的益处不大。要理解今天的专栏,您需要掌握基本的数据库常识。
顺便说一下,以后我们将向您介绍如何通过编程的方式来创建数据库、表和字段。但不是今天。
|
首先,让我们看一个脚本,该脚本可从 Microsoft Access 数据库中读取数据。此特定脚本与文件 C:\Scripts\Inventory.mdb 连接,从名为 GeneralProperties 的表中获取数据,然后,返回该表中每个记录的 ComputerName 字段的值:
On Error Resume Next
Const adOpenStatic = 3
Const adLockOptimistic = 3
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
objConnection.Open _
"Provider=Microsoft.Jet.OLEDB.4.0; " & _
"Data Source=c:\scripts\inventory.mdb"
objRecordSet.Open "SELECT * FROM GeneralProperties" , _
objConnection, adOpenStatic, adLockOptimistic
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields.Item("ComputerName")
objRecordSet.MoveNext
Loop
正如您所见,该脚本本身是非常短的(并且大部分都是可以原封不动的样板化的代码)。可是有几行代码可能有点难懂,不过不要担心:我们将详细解释它是如何运行的以及为什么这样运行。
没问题:这就是我们的工作。
该脚本首先定义一对常量(adOpenStatic 和 adLockOptimistic),然后将每个常量的值设置为 3
常量 adOpenStatic 用于确定记录集的“游标类型”。现在,我们只能说游标类型决定了您遍历记录集的方式及记录集是否动态响应数据库的更改。(就是说,假设您从数据库中检索信息,然后其他人向该数据库中添加了一个记录。那么该记录会动态显示在您的记录集中吗?还是您是否必须重新检索数据库才能得到更新的信息?)
adOpenStatic 游标允许您向前和向后遍历记录集中的记录,但其本身在数据库更改时不能自动刷新。这样可以减少处理时间和网络通信量,因为检索完记录集之后,脚本就可以“放松”一下了,而不必时刻监视数据库的更改。
游标类型及其常量,以及常量值如下表所示:
|
游标类型
|
常量
|
值
|
说明
|
|
仅向前游标
|
adOpenForwardOnly
|
0
|
只允许在记录集中向前移动。不能用于查找单个记录。不返回记录集中记录的数量。
|
|
键集游标
|
adOpenKeyset
|
1
|
允许在记录集中向前和向后滚动。支持查找功能并返回记录个数。可动态响应现有记录的更改,但无法对新记录的创建做出响应。
|
|
动态游标
|
adOpenDynamic
|
2
|
允许在记录集中向前和向后滚动。支持查找功能并返回记录个数。可动态响应记录集中的所有更改。
|
|
静态游标
|
adOpenStatic
|
3
|
允许在记录集中向前和向后滚动。支持查找功能并返回记录个数。不动态响应记录集中的更改。
|
其他常量 - adLockOptimistic - 用于配置记录的“锁定类型”。锁定类型决定数据库如何(或是否)对当前查看的记录进行锁定,这样您就会享有修改该记录的独占权限。锁定类型及其常量,以及常量值如下表所示:
|
锁定类型
|
常量
|
值
|
说明
|
|
只读游标
|
adLockReadOnly
|
1
|
通过阻止对记录的锁定来释放系统资源。然而,这会产生一个只读的记录集。
|
|
保守式记录锁定
|
adLockPessimistic
|
2
|
编辑时立即锁定记录,直到调用 Update 方法时才解除锁定。
|
|
开放式记录锁定
|
adLockOptimistic
|
3
|
只在调用 Update 方法时锁定记录。
|
|
开放式批更新
|
adLockOptimisticBatch
|
4
|
用于批更新模式。
|
定义常量之后,下一步我们创建一对对象:ADODB.Connection 对象和 ADODB.Recordset 对象。两个对象都恰如其名:Connection 对象负责管理和维护与数据库的连接,而 Recordset 对象用作由查询所返回的数据的容器。只需运行下面两行代码就可创建这两个对象:
Set objConnection = CreateObject("ADODB.Connection")
Set objRecordSet = CreateObject("ADODB.Recordset")
只要创建了一个 Connection 对象,我们就可以与数据库进行连接了。在今天的文章中,我们用一个文件路径绑定到数据库;在以后的文章中,我们将向您展示如何通过 DSN(数据源名称)绑定到数据库。下面是连接到 C:\Scripts\Inventory.mdb 数据库的代码:
objConnection.Open _
"Provider=Microsoft.Jet.OLEDB.4.0; " & _
"Data Source=c:\scripts\inventory.mdb"
是不是很简单?我们所做的就是调用 Open 方法,并向它传递一对参数:Provider 的名称 (Microsoft.Jet.OLEDB.4.0) 和 Data Source(或者文件路径 - 在本例中)。运行该代码,就可与 Inventory.mdb 数据库建立连接。
只要建立了连接,您就可通过发出 SQL 查询与数据库进行交互。我们从一个很基本的查询开始:我们想从一个名为 GeneralProperties 的表中选择所有的属性。这意味着 SQL 查询将如下所示:
SELECT * FROM GeneralProperties
然后,将该查询与 Recordset 对象和 Open 方法结合:
objRecordSet.Open "SELECT * FROM GeneralProperties" , _
objConnection, adOpenStatic, adLockOptimistic
您可以看到,我们指定了四个参数:
-
SQL 查询。(是的,除了 Select 查询之外,您可以使用 Update 查询、Insert Into 查询及其他类型的查询。)
-
数据库连接的对象引用 (objConnection)。
-
我们要使用的游标类型(静态游标,由常量 adOpenStatic 表示)。
-
我们要使用的锁定类型(开放式记录锁定,由常量 adLockOptimistic 表示)。
发出该命令后,我们得到的返回数据将会存储在内存中并在 Recordset 对象中。要遍历返回的集合,需完成以下两个任务:调用 MoveFirst 方法,并设置一个 Do Until 循环来真正遍历该记录集中的所有记录。
使用 MoveFirst 方法将游标定位在记录集的开头。坦白地讲,我们不确定您是否必须进行该操作,就我们所知游标是自动定位在记录集的开头的。但是,保险一些总比事后后悔好一些。
顺便说一下,“将游标定位在记录集的开头”的意思就是将游标指向集合的第一个记录:游标用于跟踪您在记录集中的位置。我们现在所做所有工作就是,确保在开始遍历该集合时,我们是从记录 1 开始的。如果由于某种原因,我们想要访问记录集的最后一个记录时,我们可以调用 MoveLast 方法。当然,也可以使用 Find 方法,快速搜索记录集中的任何记录。但是,这里我们有点超前了。
|
脚本专家提示。默认情况下,我们得到的返回记录的顺序与记录添加到数据库的顺序相同:返回的数据不进行排序。
除非我们想将其排序。要排序数据,需添加一个 ORDER BY 子句并指定我们排序要依据的字段名称和排序的类型(ASC 为升序,DESC 为降序)。例如,下面的 SQL 查询返回按 Manufacturer 并以升序(A - Z)排序的数据:
SELECT * FROM GeneralProperties ORDER BY Manufacturer ASC
而下面这行代码首先将数据按 Manufacturer 排序,然后再按 ComputerName 排序:
SELECT * FROM GeneralProperties ORDER BY Manufacturer ASC, ComputerName ASC
|
接下来,我们设置一个 Do Until 循环,该循环可逐个遍历集合中的每一个记录:
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields.Item("ComputerName")
objRecordSet.MoveNext
Loop
此处是脚本编写的初学者经常遇到问题的地方,所以在这应记录一些要点。
那好,我们会等您准备好纸。不,对不起:我们不可能为每个人都准备笔。那边那个人有多余的笔。
好了,准备好了吗?首先,注意 Do Until 循环,该循环一直运行到 objRecordset.EOF 为 True 时结束。EOF 为“End Of File”的缩写,表示我们应该从记录集的第一个记录开始,直至遍历到最后一个记录为止。此时,脚本将自动跳出循环。
在此有一件事要注意,如果您尝试遍历包含零记录的记录集,ADO 将会生成一个错误。您可以使用 On Error Resume Next 语句避免该错误。或者,在进入 Do Unti 循环之前,您可以检查 RecordCount 属性(该属性指示记录集中记录的个数)的值。如果 RecordCount 的值等于 0,则记录集中没有记录,因此就没有必要执行 Do Until 循环了。要根据 RecordCount 的值决定是否进行遍历,您可以将脚本做如下修改:
If objRecordset.RecordCount <> 0 Then
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields.Item("ComputerName")
objRecordSet.MoveNext
Loop
End If
下面是另一个要点。如果您已经编写过很多 WMI 脚本或 ADSI 脚本,您很可能同样使用过 For Each 循环来遍历集合。For Each 循环非常好用,因为它们非常灵活:For Each 循环“知道”它需要查看集合中的每一项。例如,以下就是遍历 colServices 集合并回显每个服务的名称的所有代码:
For Each objService in colServices
Wscript.Echo objService.Name
Next
Do Until 循环不那么灵活;然而,我们仍然要使用它,因为 For Each 循环不能处理返回的记录集。请记住:Do Until 循环并不会自动遍历记录集中的每一个记录。因此,您必须专门在代码中告诉脚本移动到下一个记录。这就是 MoveNext 方法的用途:
如果您遗漏了这行代码会发生什么情况呢?脚本从记录 1 开始,到达 Loop 语句,经过再循环,然后又从记录 1 开始。它将到达 Loop 语句,再循环,然后仍然再从记录 1 开始。这将永无休止地继续下去。换句话说,在下一次循环之前,调用 MoveNext 方法至关重要。只有这样才能遍历记录集中的每一个记录。
最后,我们需要知道在 Do Until 循环中如何引用一个数据库字段:
Wscript.Echo objRecordSet.Fields.Item("ComputerName")
正如您所见,我们不能只回显字段名称;这样不行:
Wscript.Echo ComputerName
我们需要提供完整的对象路径:objRecordSet.Fields.Item("ComputerName")。看起来有点复杂,但是,请记住,大部分代码都是样板化的。只有字段名称本身需要您做一下更改:Wscript.Echo objRecordSet.Fields.Item("ComputerName")。只不过是多敲几个字,与只回显字段名称一样简单。
现在,从命令提示符运行您的脚本,根据数据库中存储的记录的不同,您就会得到一份计算机名称列表:
atl-ws-01
atl-ws-02
atl-ws-03
atl-ws-04
atl-ws-05
太美妙了,不是吗?
我们知道您正在想什么:所有这一切就是为了从数据库中读取一些计算机名称吗?当然,今天我们并没有做什么了不起的事情。但主要是因为我们想解释许多基础知识:游标及锁定类型的作用、如何遍历一个记录集、如何引用记录集中的一个字段。既然这些都不成问题了,我们要找点真正乐事了。
哦,不,很抱歉:我们是指真正的数据库的乐事。但是这和您原来所想的差不多。