数据库损坏:MS Access数据库中丢失/重复记录



问题

我有一个使用Microsoft Access作为后端的VB6应用程序。该应用程序可在多用户环境中使用。最近,在没有对应用程序进行任何更改的情况下,我们看到在数据库的一个表中,有些记录没有保存,而其他记录保存了两次,有时甚至保存了3次。

这是一个使用Access 2002作为后端的VB6应用程序。该应用程序安装在运行Windows 2008服务器的计算机上。网络上的多个用户在他们的计算机上有一个应用程序的快捷方式,他们同时运行应用程序,访问相同的数据库但不同的记录。

应用程序使用以下逻辑将记录保存到数据库:

1
If objectID > 0
' existing record
sql = "UPDATE myTable SET a=..., b=..., etc WHERE Id = objectID"
cn.Execute sql  
Else
' new object; create new record
nextID = "SELECT Max(id) + 1 FROM myTable"
sql = "INSERT INTO myTable (a,b,c) VALUES (...)"
cn.Execute sql
objectID = nextID
End If
Exit Function 
Err_Handler:
' handle the case where two people get the same ID
If timeNotExpired Then
' Try saving again;
Resume 1
Else
' Could not save; display error 
End If

因此,当保存记录时,如果它存在,则为UPDATED,否则为insert。主键字段通过调用Max(ID) + 1获取。使用这种设置,Max(ID) + 1可能会为同时保存到同一表的两个用户返回相同的ID。如果发生这种情况,应用程序将返回到标签1所在的位置,并再次调用Max(ID) + 1,直到没有冲突或保存操作超时为止。简单。

上周,出乎意料地,没有对应用程序进行任何更改,它刚刚开始发生(1)一个表中的记录将随机不保存,或(2)同一表中的给定记录将在数据库中出现两次甚至3次。换句话说,该表中的一条记录将在数据库中出现多次。

这种情况并不总是发生,但每天会发生5-10次。请注意,全天至少有5个人使用该应用程序,主要用于数据输入目的。如果给定的记录没有正确保存,则数据不同步,应用程序将显示一条消息。此时,如果检查数据库,将看到一条记录要么缺失,要么重复。通常,当它发生在一个人身上时,它也会发生在其他输入数据的用户身上。同时。

编辑让我再补充一点背景……我有两个表(以及其他表),它们表示客户/订单场景中的父/子关系。父类必须至少有一个子类,并且应用程序有适当的检查,以确保除非用户为父类添加了至少一个子类,否则父类不会被保存到数据库中。如果用户添加了没有任何子节点的父节点,则不能继续对应用程序执行任何操作。保存父母(和孩子)的数据库代码有一个if语句,该语句读取"if parentHasNoChildren Exit Function"一行的内容。绝对没有办法,绝对没有办法,而且……琴不…道路对于应用程序运行的代码,该代码将产生一个父节点,该父节点被保存到数据库中,没有子节点。

但是,唉,从上周开始,在完全没有修改应用程序的情况下,我们看到数据库中没有孩子的父母。每个用户每天大约会出现10次这样的问题。

我已经修改了应用程序,以便它在发现没有子节点的父节点时提醒用户。如果是,程序指示他们删除记录并重新添加,之后一切正常。

现在,父母没有孩子就到达数据库的事实只能意味着(1)应用程序试图保存孩子,(2)访问没有返回错误,表现得像一切正常,(3)应用程序"认为一切都很好",而实际上孩子根本没有被保存。我知道Access没有返回任何错误,因为应用程序记录保存操作期间发生的每个错误。我检查了日志,没有关于孩子没有被保存的错误。


编辑2:(我相信我找到了问题!)

检查数据库后,我发现子表中的主键不见了。也就是说,应该设置为主键的字段在那里,但它没有设置为主键。我不知道这是怎么发生的。数据库设计没有被改动,所以我假设MS Access有一天醒来说:"嗯,我想知道如果我从这个表中删除主键会发生什么……">

在任何情况下,我相信这绝对是我的问题的原因。设置主键以防止重复条目。密钥消失后,可以保存具有相同ID的两个子记录。由于我的代码使用Max(ID) +1为新的子记录生成ID,因此Max(ID) +1可能会为同时尝试保存子记录的多个用户返回相同的ID。在过去,这不会是一个问题,因为Access会产生一个关于重复ID的错误,应用程序会检测到这个错误,然后再次执行Max(ID) +1。但是如果没有主键,则将用相同的ID保存两个子记录。之后,如果任何用户对其中一条记录进行了更改,那么这两条记录都将被更新,并且这两条记录的所有字段(包括父记录的外键parentID)都将被设置为相同的值。这将导致父母一方没有孩子,而另一方有重复的孩子。天哪,真是一团糟!!我只是试着添加主键到表,我不能,因为有重复的记录,我必须找到和删除。我将在能够添加主键后将最终结果作为答案发布。谢谢你的帮助。


最后一点说明:所讨论的表是数据库中最大的表,包含超过350万条记录。该表有22个字段,其中20个是长整数,一个是文本字段,字段大小为100。另一个是布尔字段。

What I've Done

由于应用程序没有更改,我立即假设(并继续假设)问题是MS Access数据库中的损坏。我做了以下的事情:

  1. 每天压缩数据库
  2. 创建一个新数据库,并将表从旧数据库导入到新数据库。
  3. 创建一个新数据库,每次只导入一个表的定义,然后使用追加查询将数据转移到新数据库。
  4. 确保我有最新的Office服务包
  5. 确保连接对象和记录集被正确关闭/处理
  6. 阅读并实施微软文章中关于如何保持Jet数据库处于最佳工作状态的详细建议。

我还将仔细检查应用程序,看看是否发现了什么,尽管所有的东西都指向Access是罪魁祸首

任何想法?

以前有人遇到过这样的情况吗?大约10年前,我自己也遇到过类似的问题。当时,我得到了"无法识别的文件格式"错误,这是一个明显的数据库损坏的情况。我通过创建一个新数据库并导入表来修复它,但这一次没有帮助。什么好主意吗?

我会检查ID列的数据类型,并确保它足够大。考虑将其更改为计数器数据类型,而不是运行域函数(MAX(ID)),或者如果可能的话更改为replicationID。这就是你的问题可能发生的地方。

我之前已经将ID设置为LONG,并在另一个表中维护了我自己的计数器。也许是保存NextID列的表,以及获取该列并为下一个人更新+ 1的VBA函数。在事务中,这可能比MAX更可靠,因为MAX必须与锁相抗衡。

祝你好运!

问题源于Microsoft Access删除了数据库中两个表的主键。因为我使用Max(ID) + 1来获取新记录的ID,而且有多个用户同时创建新记录,所以同一个ID有时会用于多条记录。这就导致了上面提到的问题。

为了解决这个问题,我只是在删除了我发现的所有重复条目后重新添加了键。

我也会像@DanielG建议的那样,在新记录中尽量避免使用Max(ID) + 1

如果其他人不幸在多用户环境中使用MS Access,我建议遵循微软在文章中概述的建议如何保持Jet 4.0数据库处于最佳工作状态。

谢谢大家的帮助!

最新更新