我想比较两个 ms-access .mdb 文件以检查它们包含的数据是否相同。
我怎样才能做到这一点?
我已经在代码中多次做过这种事情,主要是在本地 MDB 需要从网站上输入的数据中应用更新的情况下。在一种情况下,该网站由 MDB 驱动,在另一种情况下,它是 MySQL 数据库。对于 MDB,我们刚刚下载了它,对于 MySQL,我们在网站上运行脚本来导出和 FTP 文本文件。
现在,重点是我们想将本地 MDB 中的数据与从网站下载的数据进行比较,并更新本地 MDB 以反映网站上所做的更改(不,不可能使用单个数据源 - - 这是我建议的第一件事,但这是不可行的)。
让我们将 MDB A 称为您的本地数据库,并将 MDB B 称为您正在下载以进行比较的数据库。您需要检查的是:
存在于 MDB A 但不存在于 MDB B 中的记录。这些可能会或可能不会被删除(这取决于您的特定数据)。
MDB B 中存在但 MDB A 中不存在的记录。这些记录将从 MDB B 附加到 MDB A。
两者中都存在的记录,需要逐个字段进行比较。
步骤#1 和#2 很容易通过使用外连接查找丢失记录的查询来完成。第 3 步需要一些代码。
代码背后的原理是两个 MDB 中所有表的结构都是相同的。因此,您使用 DAO 遍历 TableDefs 集合,打开记录集,然后遍历 fields 集合以在每个表的每一列上运行 SQL 语句,以更新数据或输出差异列表。
代码背后的基本结构是:
Set rs = db.OpenRecordset("[SQL statement with the fields you want compared]")
For Each fld In rs.Fields
' Write a SQL string to update all the records in this column
' where the data doesn't match
strSQL = "[constructed SQL here]"
db.Execute strSQL, dbFailOnError
Next fld
现在,这里的主要复杂性是每个字段的 WHERE 子句必须不同——文本字段需要与数字和数据字段区别对待。因此,您可能需要一个 SELECT CASE 来根据字段类型编写 WHERE 子句:
Select Case fld.Type
Case dbText, dbMemo
Case Else
End Select
您将希望使用 Nz() 来比较文本字段,但您将使用 Nz(TextField,'') 来比较文本字段,而将 Nz(NumericField,0) 用于数字字段或日期字段。
我的示例代码实际上并没有使用上面的结构来定义 WHERE 子句,因为它仅限于与 ZLS(文本字段)连接时效果很好的字段。下面的内容读起来相当复杂,但它基本上是对上述结构的扩展。
它是为了更新效率而编写的,因为它对表的每个字段执行 SQL UPDATE,这比对每一行执行 SQL UPDATE 效率高得多。另一方面,如果您不想进行更新,但想要一份差异列表,您可能会以不同的方式对待整个事情。但这会变得相当复杂,具体取决于输出,
如果您只想知道两个 MDB 是否相同,则首先检查每个表中的记录数,如果有一个不匹配,则退出并告诉用户 MDB 不相同。如果记录数相同,那么您必须逐个字段检查,我认为最好使用动态编写的逐列 SQL 来完成——只要结果 SQL SELECTS 之一返回 1 个或多个记录,您就中止并告诉您的用户 MDB 不相同。
复杂的部分是,如果您想记录差异并通知用户,但是这样做会使这个已经无休止的帖子变得更长!
下面是一个更大的子例程的一部分代码,它使用来自 qdfNewMembers(来自 MDB B)的数据更新保存的查询 qdfOldMembers(来自 MDB A)。第一个参数 strSQL 是一个 SELECT 语句,仅限于您要比较的字段,而 strTmpDB 是另一个 MDB(在我们的示例中为 MDB B)的路径/文件名。该代码假定 strTmpDB 已经创建了 qdfNewMembers 和 qdfOldMembers(原始代码即时写入保存的 QueryDef)。它也可以是直接表名(我使用保存查询的唯一原因是字段名在为其编写的两个 MDB 之间不完全匹配)。
Public Sub ImportMembers(strSQL As String, strTmpDB As String)
Const STR_QUOTE = """"
Dim db As Database
Dim rsSource As Recordset '
Dim fld As Field
Dim strUpdateField As String
Dim strZLS As String
Dim strSet As String
Dim strWhere As String
' EXTENSIVE CODE LEFT OUT HERE
Set db = Application.DBEngine(0).OpenDatabase(strTmpDB)
' UPDATE EXISTING RECORDS
Set rsSource = db.OpenRecordset(strSQL)
strSQL = "UPDATE qdfNewMembers INNER JOIN qdfOldMembers ON "
strSQL = strSQL & "qdfNewMembers.EntityID = qdfOldMembers.EntityID IN '" _
& strTmpDB & "'"
If rsSource.RecordCount <> 0 Then
For Each fld In rsSource.Fields
strUpdateField = fld.Name
'Debug.Print strUpdateField
If InStr(strUpdateField, "ID") = 0 Then
If fld.Type = dbText Then
strZLS = " & ''"
Else
strZLS = vbNullString
End If
strSet = " SET qdfOldMembers." & strUpdateField _
& " = varZLStoNull(qdfNewMembers." & strUpdateField & ")"
strWhere = " WHERE " & "qdfOldMembers." & strUpdateField & strZLS _
& "<>" & "qdfNewMembers." & strUpdateField & strZLS _
& " OR (IsNull(qdfOldMembers." & strUpdateField _
& ")<>IsNull(varZLStoNull(qdfNewMembers." _
& strUpdateField & ")));"
db.Execute strSQL & strSet & strWhere, dbFailOnError
'Debug.Print strSQL & strSet & strWhere
End If
Next fld
End If
End Sub
函数 varZLSToNull() 的代码:
Public Function varZLStoNull(varInput As Variant) As Variant
If Len(varInput) = 0 Then
varZLStoNull = Null
Else
varZLStoNull = varInput
End If
End Function
我不知道这是否太复杂以至于没有意义,但也许它会对某人有所帮助。
您可以尝试AccessDiff(付费产品)。它具有比较模式、数据以及访问对象的能力。它有一个 GUI 和一个命令行界面。
披露:我是这个工具的创造者。
获取数据库表的文本转储,并使用 BeyondCompare(或任何其他文本比较工具)简单地比较转储的文本文件。粗制滥造,但可以工作!
我对Cross-Database Comparator有很好的经验。它能够比较结构和/或数据。
请参阅我网站上Microsoft Access 第三方实用程序、产品、工具、模块等页面上的比较 Access 数据库部分。
不久前,我在我的accdbmerge实用程序中添加了“table diff”功能。我相信这个答案无助于解决原始问题,但它可能对将来面临同样问题的人有所帮助。
如果您想知道文件是否相同,那么
fc file1.mdb file2.mdb
在 DOS 命令行上。
如果文件不相同,但您怀疑它们包含相同的表和记录,那么最简单的方法是快速编写一个小实用程序,打开两个数据库并循环遍历执行异构查询的表以提取两者之间的差异文件。
有一些工具可以为您执行此操作,但它们似乎都是共享软件。