DAO.Recordset.Update 导致记录锁

Posted

技术标签:

【中文标题】DAO.Recordset.Update 导致记录锁【英文标题】:DAO.Recordset.Update results in reckord lock 【发布时间】:2011-11-01 10:58:11 【问题描述】:

我正在尝试运行以下代码来循环记录集并在必要时进行更新。

我有一个连接到 mysql 后端的 Microsoft Access 数据库。每当我运行此代码时,我都会收到以下错误:

3197 错误:Microsoft Office Access 数据库引擎停止了进程,因为您和其他用户正试图同时更改相同的数据。

代码如下:

Private Sub test()
    Dim rs As DAO.Recordset, rsCnt As Long, i As Long

    Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
    rs.MoveLast
    rsCnt = rs.RecordCount
    rs.MoveFirst
    For i = 1 To rsCnt
        rs.Edit
        rs!MyFieldInTable = "test"
        rs.Update
    Next i
End Sub

我认为 Access 数据库可能已损坏,因此我提取了一个较早的备份,但它正在做同样的事情,这让我认为这是一个 MySql 问题。

我们在这个数据库的另一个版本上使用了一段相同的代码,链接到一个不同的 MySql 表,它工作正常。

此外,当我打开查询记录集时,我可以毫无问题地编辑查询中的数据。

只需添加,在第一个循环中,rs!MyFieldInTable 已更新,然后我收到错误消息。

【问题讨论】:

【参考方案1】:

您似乎没有移动到记录集中的另一条记录。简单地增加i 不会移动到下一条记录。更传统的方法是迭代记录集而不需要其他变量(irsCnt)。

Dim rs as DAO.Recordset
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop

编辑 经过一番搜索,我遇到了this thread,这似乎与您的问题相似。在线程的底部,建议通过选择“高级”选项卡并选择“返回匹配行”选项来修改 MySQL DSN 的 ODBC 设置。该帖子还说要删除链接表,然后将其重新链接到您的 Access 数据库。 我以前没有使用过 MySQL 的 Access,所以我不知道这是否有效,所以请谨慎行事!

您也可以尝试更改您的记录集以将 dbOptimistic 标志用于记录集锁定选项,看看是否有帮助:

set rs = CurrentDB.OpenRecordSet("qryMyQuery", DB_OPEN_DYNASET, dbOptimistic)

【讨论】:

+1 与@XIVSolutions 一样,当我收到已加载新答案的消息时,我正在写一个类似的答案。 另外,如果我错了,请纠正我,但我相信 rs.Edit 必须进入循环。来自 DAO 帮助文件:编辑方法: 当前记录从可更新的 Recordset 对象复制到复制缓冲区以供后续编辑。 最好使用 DAO.Database 对象变量的 .Execute 方法并包含@mwolfe02 描述的dbFailOnError。还要添加一个错误处理程序来处理任何 dbFailOnError 表面。 DoCmd.RunSQL 的一个后果是人们经常关闭 SetWarnings 以避免确认消息。关闭 SetWarnings 时,您可能会隐藏重要信息,这会妨碍故障排除工作。 我想重申一下@HansUp 刚才所说的话。如果您养成使用带有 dbFailOnError 的 .Execute 而不是 Warnings False/RunSQL/Warnings True 方法的习惯,总有一天您会省去很多心痛。我知道没有充分的理由使用 DoCmd.RunSQL。 @Hk1:我同意,虽然这个示例的目的不是展示通常用生产质量代码编写的全文,它只是为了说明如何在没有使用整数作为海报最初用示例代码说明的。【参考方案2】:

您可以尝试两件事。首先,尝试在打开记录集时添加 dbSeeChanges 选项:

Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", dbOpenDynaset, dbSeeChanges)
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop

正如@HansUp 建议的那样,另一个选项是使用 SQL 更新语句而不是动态记录集。关键是将记录集作为快照打开,这样您对记录所做的更改不会影响记录集本身。

Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryBatchPayments", dbOpenSnapshot)
Do Until rs.EOF
    db.Execute "UPDATE Payments " & _
               "SET DCReference='test' " & _
               "WHERE PaymentID=" & !PaymentID, dbFailOnError
    rs.MoveNext
Loop

【讨论】:

【参考方案3】:

我遇到了同样的问题,结果证明我的解决方案是 BIT(1) 字段的默认值。 Access 不喜欢这些为空。确保在 mysql 中为这些字段使用 0 或 1。

【讨论】:

【参考方案4】:

我这里没有 MySQL 来尝试这个,但在我看来你的代码在 rs.Update 方法执行后没有推进记录集,所以你试图更新相同的字段第一记录。

在 rs.Update 之后添加这一行:

rs.MoveNext

希望对您有所帮助。

【讨论】:

此外,Tim Lentine 发布的代码是一种更优雅的方式。他在我还在写答案的时候发帖。 如果人们要投反对票,那么如果他们有理由在投反对票的同时进行投票,那就太好了。只是说'【参考方案5】:

尝试从设置为 CurrentDb() 的对象变量调用 OpenRecordset,而不是直接从 CurrentDb() 调用。

Dim rs as DAO.Recordset
Dim db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop

该建议的原因是我发现对 CurrentDb 的操作直接会引发有关“未设置块”的错误。但是在使用对象变量时我没有得到错误。 ISTR OpenRecordset 就是这样一个问题所在。

另外,我的印象是你的方法是一种繁琐的方式来完成相当于:

UPDATE qryMyQuery SET FieldNameHere = "test";

但是,我怀疑该示例是记录集方法有用的真实世界情况的代理。尽管如此,我还是想知道您在执行 UPDATE 语句时是否会看到相同或不同的错误。

如果您仍然遇到此问题,向我们展示 qryMyQuery 的 SQL 视图可能会有所帮助。

【讨论】:

【参考方案6】:

我发现如果尝试保存与 MySql 记录 Access 中已有的数据相同的数据,则会显示这种错误。我已经尝试了这个线程的一些建议,但没有帮助。

对此的简单解决方案是使用手动时间戳来保存略有不同的数据。这是一个将排序顺序字段设置为 10、20、30...

    i = 10
    timeStamp = Now()
    Do Until Employee.EOF
        Employee.Edit
        Employee!SortOrderDefault = i
        Employee!LastUpdated = timeStamp
        Employee.Update
        i = i + 10
        Employee.MoveNext
    Loop

我在MySql表中尝试过自动时间戳,但是当新条目数据与旧条目数据相同时没有帮助。

【讨论】:

我也有同样的经历。在这个 MySQL 错误报告中提到:bugs.mysql.com/bug.php?id=46406 这似乎是由 Access 形成其更新查询的方式引起的。可以像您建议的客户端或使用timestamp 列的服务器端一样解决。【参考方案7】:

我的小提示是,在将 SQL 表链接到 Microsoft Access 时,位是非常、非常、非常糟糕的数据类型,因为只有 SQL Server 了解位是什么,Microsoft Access 很难解释位是什么。将任何位数据类型更改为 int(整数)并重新链接应该清除的表。此外,请确保您的布尔值始终在您的 VBA 代码中包含 1 或 0(不是 yes/no 或 true/flase),否则链接 SQL 表的更新将失败,因为 Microsoft Access 将尝试使用 True 更新它们/False 或 Yes/No 和 SQL 不会这样。

【讨论】:

【参考方案8】:

我也有同样的问题;我解决了他们使用 dao.recordset 将这些添加到代码中的问题:

**rst.lockedits = true**
rst.edit
rst.fields(...).value = 1 / rst!... = 1
rst.update
**rst.lockedits = false**

这似乎解决了刚刚打开的数据(例如在表单中)和使用代码更新它们之间的冲突。

对不起,我的英语不好...我读了很多书,但我从来没有学过!我只是个意大利人。

【讨论】:

如果您已将代码更改为 SQL 语句,请注意。 Form_Unload 事件尝试更新您已通过 SQL 更改的相同数据并引发不可管理的错误;这可以使您的所有应用程序化。

以上是关于DAO.Recordset.Update 导致记录锁的主要内容,如果未能解决你的问题,请参考以下文章

在进行游标提取时,如何判断哪个记录导致错误?

由于加入表而导致的记录重复

是啥导致 Access 2010 中出现“在任何记录中未找到搜索键”错误?

记录一次异步导致导致res获取到的数据为空

处理 BigQuery 中未嵌套记录导致的重复行的最佳做法?

添加表单返回 None 作为记录 id,导致 URL 出错