简体   繁体   English

尝试通过 DAO 记录集更新记录时出现运行时错误 3197

[英]Run-time error 3197 when attempting to update record via DAO recordset

I've got a Microsoft Access 2016 application, currently running with a SQL Server 2019 backend.我有一个 Microsoft Access 2016 应用程序,目前使用 SQL Server 2019 后端运行。 I'm having issues attempting to use a recordset.update to save changes to a particular record in my products table.我在尝试使用 recordset.update 将更改保存到产品表中的特定记录时遇到问题。 I'm entirely self-taught and have been coding with vba for around 18 months, came across this issue with bit fields in the past, but I can't see what's causing it this time.我完全是自学成才,并且已经使用 vba 编码大约 18 个月,过去曾遇到过位字段的这个问题,但这次我看不出是什么原因造成的。

The error:错误:

Error 3197: the microsoft access database engine stopped the process because you and another user are attempting to change the same data at the same time错误 3197:microsoft access 数据库引擎停止了进程,因为您和另一个用户试图同时更改相同的数据

Things I have tried:我尝试过的事情:

  1. The table has a primary key该表有一个主键

  2. Most records in this table allow me to edit and update the changes (there's a random selection that will not work)此表中的大多数记录允许我编辑和更新更改(随机选择不起作用)

  3. All bit fields have default values set to 0所有位字段的默认值都设置为 0

  4. I've tried only updating 1 of the many fields to see if that effects the outcome, it doesn't, no matter if I'm editing a integer field only, or multiple mixed types, it still occurs我尝试只更新众多字段中的 1 个以查看这是否会影响结果,它不会,无论我是仅编辑 integer 字段还是多个混合类型,它仍然会发生

  5. I can directly change the values of any field in this table using a SQL Query (pass through or via Access query designer)我可以使用 SQL 查询(通过或通过 Access 查询设计器)直接更改此表中任何字段的值

  6. I cannot change any value on these records (others are fine) via the table directly (using ms access table view, I get the Drop Changes / Save to clipboard dialog box)我无法直接通过表更改这些记录上的任何值(其他都可以)(使用 ms 访问表视图,我得到 Drop Changes / Save to clipboard 对话框)

  7. Although this does happen when using this particular function via a form, even when running the code directly via the immediate window in the vba editor, I get the same error.尽管在通过表单使用此特定 function 时确实会发生这种情况,但即使直接通过 vba 编辑器中的立即 window 运行代码,我也会收到相同的错误。 But I ensured that the record locking used by the form was no locks.但是我确保表单使用的记录锁定是没有锁的。

  8. The default open mode is set to shared (for the db, via Options -> Client Settings)默认打开模式设置为共享(对于 db,通过选项 -> 客户端设置)

  9. The default record locking is no locks (for the db, via Options -> Client Settings)默认记录锁定是无锁(对于数据库,通过选项 -> 客户端设置)

  10. Currently using Row-Level Locking (for the db, via Options -> Client Settings)当前使用行级锁定(对于数据库,通过选项-> 客户端设置)

For reference, the code is simple and straightforward enough:作为参考,代码足够简单明了:

Dim dbs As DAO.Database
Dim productRS As DAO.Recordset

Set dbs = CurrentDb
Set productRS = dbs.OpenRecordset("Select * From tblProducts WHERE ProductID = " & ProductID, dbOpenDynaset, dbSeeChanges)

With productRS
.Edit
!Name = someName
.Update
End With

rs.close
dbs.close

set rs = nothing
set dbs = nothing

I also have a RecordTimestamp field (datetime2(7)) that uses GetDate() as a default value for the record, and it's been set this way from the beginning of the project which leads me to think it's not an issue with this field.我还有一个 RecordTimestamp 字段 (datetime2(7)),它使用 GetDate() 作为记录的默认值,并且从项目一开始就是这样设置的,这让我认为这不是这个字段的问题。 But I don't fully understand how exactly sqlserver and access remember/determine if a record is locked/being updated or if there's a way I can clear all these pending/non-existant locks.但是我不完全理解 sqlserver 和访问如何准确地记住/确定记录是否被锁定/正在更新,或者是否有办法清除所有这些挂起/不存在的锁。

Thanks in advance if anyone can think of something that would be causing this issue.如果有人能想到会导致此问题的事情,请提前致谢。

You need to add what is called a rowverison column to the database.您需要将所谓的 rowverison 列添加到数据库中。 now perhaps the greatest award in history goes to the folks for having called this a "timestamp" column, thuse the WORST POSSBILE name!现在,也许历史上最伟大的奖项颁给了人们,因为他们称之为“时间戳”列,因此是最糟糕的名字!

The reason of course is a timestamp column HAS ZERO to do with date time, and is a VAST difference of a column then that of a datetime column in SQL server.原因当然是时间戳列与日期时间的关系为零,并且是 SQL 服务器中的列与日期时间列的巨大差异。

Timestamp data type column is NOT a date time column时间戳数据类型列不是日期时间列

It is a column that you NEVER EVER in ANY WAY and in ANY context WILL EVER be set, or touch or modify by you, or by any code.这是一个您永远不会以任何方式和任何上下文设置、触摸或修改的列,您或任何代码。 And you NEVER have to set a default value for this row version column, and you don't ever try to touch or modify this column - and that includes your code, sql server side code, and that also includes NOT having a default set for this timestamp column, which of course is HOW you create and obtain a ROWVERSION column in Access.而且您永远不必为此行版本列设置默认值,并且您永远不会尝试触摸或修改此列 - 其中包括您的代码、sql 服务器端代码,还包括没有为这个时间戳列,当然是您如何在 Access 中创建和获取 ROWVERSION 列。

it is a column that DOES NOT have a default.它是一个没有默认值的列。 It is in fact a binary blob with a checksum value.它实际上是一个带有校验和值的二进制 blob。

To be 100% if not 1000% clear?如果不是 1000% 清楚,是 100%?

You are being told to add what is called a ROWVERSION column.您被告知添加所谓的 ROWVERSION 列。 To create such a column, you create a column of TIMESTAMP data type.要创建这样的列,您需要创建一个 TIMESTAMP 数据类型的列。 This is NOT datetime column, and you are NOT to create a datetime column, nor are you to set a default for this row version column.这不是日期时间列,您不能创建日期时间列,也不能为此行版本列设置默认值。 Nor are you to adopt some datetime column with a datetime(2)7 format.您也不能采用 datetime(2)7 格式的一些 datetime 列。

Of course any floating point columns also need a default value of 0, and they should not be null in addition to having that defaulted value.当然,任何浮点列也需要默认值 0,除了具有该默认值之外,它们不应该是 null。

Last but not least:最后但并非最不重要的:

If the floating number values in that column have been set outside of access, then rouding issues can crop up.如果该列中的浮点数值已设置为无法访问,则可能会出现路由问题。

As a result when access attempts to check/test if the record been changed, it does a FIELD BY FIELD data compare, and it fails.因此,当访问尝试检查/测试记录是否已更改时,它会进行 FIELD BY FIELD 数据比较,但它会失败。

However, introduction of a ROWVERSION column (you use data type timestamp), then access forgoes this column by column "test" and then uses that TIMESTAMP column in one operation.但是,引入 ROWVERSION 列(您使用数据类型时间戳),然后访问逐列“test”放弃此列,然后在一次操作中使用该 TIMESTAMP 列。 Thus no column by column compare need occur anymore, and thus things like floating point numbers and due to rounding compares are not required anymore.因此不再需要逐列比较,因此不再需要浮点数和由于舍入比较等内容。

As noted, without question, the name for the data type of TIMESTAMP does get the worlds BEST award for the worst possible name.如前所述,毫无疑问,TIMESTAMP 数据类型的名称确实因最糟糕的名称而获得了世界最佳奖。 To MS's credit, there newer documentation is referring to the timestamp data type column as ROWVERSION, but the damage was and has been done.值得称赞的是,较新的文档将时间戳数据类型列称为 ROWVERSION,但损害已经发生并且已经发生。 And we for ever more live with this mess.我们将永远生活在这个烂摊子中。

So, it is VERY important to keep in mind that this "rowversion" column of data type = timestamp has ZERO ZERO ZERO to do with datetime.因此,非常重要的是要记住,这个数据类型 = 时间戳的“rowversion”列与日期时间有零零零。

Hence, one can't confuse a datetime column with that of a timestamp (aka: row version) column, they are vast different.因此,不能将日期时间列与时间戳(又名:行版本)列混淆,它们是截然不同的。

so to fix this problem, you will read weekly posts here on SO and other boards.所以要解决这个问题,你会在 SO 和其他板上阅读每周的帖子。 The simple recommendation here to for you to add a timestamp column, of which you now realize has nothing to do with time, or date, or in fact with a datetime column.这里的简单建议是为您添加一个时间戳列,您现在意识到它与时间或日期无关,或者实际上与日期时间列无关。

As noted, that rowversion column does not have a default, and you never set any such default in sql server, or in the access side of things.如前所述,rowversion 列没有默认值,并且您从未在 sql 服务器或事物的访问端设置任何此类默认值。 You don't have to and in fact as a general rule can't modify this row version column.您不必这样做,实际上通常不能修改此行版本列。 And, you don't set a default, and it is simply a binary blob column used to determine if the record been changed.而且,您没有设置默认值,它只是一个二进制 blob 列,用于确定记录是否已更改。

And as noted, having such a row version column allows access to forego and to NOT have to do a column by column compare to determine if a record been changed.并且如前所述,拥有这样的行版本列允许访问放弃,并且不必逐列比较以确定记录是否已更改。

So this weekly here is another post for the last 20 years?所以这个周刊是过去 20 年的另一篇文章?

And the solution for those 20 years?那20年的解决方案呢?

You make sure there is a PK.你确保有一个PK。

You make sure you default bit fields to 0 - don't allow null.您确保默认位字段为 0 - 不允许 null。

You should also make sure number (floating/real) columns also have that default of 0.您还应该确保数字(浮动/实数)列也具有默认值 0。

And last but not least, you also add a rowversion column, of which you need to choose "timestamp" as the data type to get and obtain a rowversion column in SQL server.最后但并非最不重要的是,您还添加了一个rowversion列,您需要选择“timestamp”作为数据类型来获取并获取SQL服务器中的rowversion列。

And as noted this name and type of column has ZERO to do with a datetime column.如前所述,此列的名称和类型与日期时间列的关系为零。

so, like the other frequent posts - about 1-2 times a week for the last 20 years?那么,就像其他频繁发布的帖子一样——过去 20 年来每周大约 1-2 次?

Try adding a timestamp column as per the many long years of recommending to do this as fix for this issue.尝试根据多年的建议添加时间戳列,以解决此问题。

There's no need to use a recordset for a simple update.无需使用记录集进行简单更新。 Just use Database.Execute只需使用Database.Execute

dbs.Execute "Update tblProducts " & _
    "Set Name = '" & someName & "' " & _
    "WHERE ProductID = " & ProductID & ";"

Debug.Print dbs.RecordsAffected & " record(s) affected."

If you insist using the recordset to perform the update, try changing the select statement from如果您坚持使用记录集执行更新,请尝试将 select 语句从

Select * From tblProducts ...

to

Select Name From tblProducts ...

Why bother dragging data back from the server that you're not planning to use?为什么要费心从服务器拖回您不打算使用的数据?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM