带有 SQL Server 后端更新的 MS Access 失败且没有错误

Posted

技术标签:

【中文标题】带有 SQL Server 后端更新的 MS Access 失败且没有错误【英文标题】:MS Access with SQL Server back-end update fails without error 【发布时间】:2019-08-11 19:54:15 【问题描述】:

这个简单的 VBA 语句不能按预期工作:

strSQl = "UPDATE Inventory SET NumberOfBlocks = BlocksReserved, LastUser = 'Me' WHERE InventoryID = 1234;"
CurrentDb.Execute strSQl, dbFailOnError + dbSeeChanges

LastUser 更新了,但 NumberOfBlocks 保持不变,没有错误。

如果我在 SSMS 中或作为 Access 查询运行此语句,它会起作用。

如果我在 VBA 语句中使用变量 ..."SET NumberOfBlocks = " & intBlocksReserved & ",...,它可以工作。

恒定工作:..."SET NumberOfBlocks = 555"...

这个也有效:NumberOfBlocks = (BlocksReserved * 1)

NumberOfBlocks 和 BlocksReserved 都是 smallint 且不为 null;记录有一个时间戳/行版本字段。

环境:Access 2016 和 SQL 2016 后端。

任何想法为什么我的初始陈述会默默地失败?谢谢!


更多测试证实了我之前的发现:

    创建了一个新的 Access db,表 Inventory:ID (AutoNumber, PK), NumberOfBlocks (Integer), BlocksReserved (Integer), LastUser (Short Text 10)

    在 SQL Server 中创建了一个表:

    [ID] [int] IDENTITY(1,1) 非空, [NumberOfBlocks] [smallint] NULL, [BlocksReserved] [smallint] NULL, [LastUser] nvarchar NULL, [RV] [时间戳] NOT NULL

    设置ID为主键,链接SQL表,两者都输入测试数据。

    在两张表上运行完全相同的代码(仅更改了表名):

    将 strSQl 变暗为字符串

    strSQl = "UPDATE Inventory SET NumberOfBlocks = BlocksReserved, LastUser = 'Me';" CurrentDb.Execute strSQl,dbFailOnError + dbSeeChanges

结果:

本地访问表:NumberOfBlocks = BlocksReserved, LastUser = 'Me'

链接的 SQL 表:NumberOfBlocks 不变,LastUser = 'Me'


更多注释:

将 SQL Server 中的数据类型更改为 int(而不是 smallint)并没有什么不同。

但是,显式转换字段是有效的:

...SET NumberOfBlocks = CInt(BlocksReserved)...

就像

...SET NumberOfBlocks = (BlocksReserved * 1)...

我猜,这将我的帖子从一个问题变成了一个提醒......

【问题讨论】:

静默失败,因为 CurrentDb.Execute 是为这种行为而设计的。为什么要用同一张表中另一个字段的相同数据更新一个字段? 这条记录的字段比较多。我不得不拆分原始记录,即我首先插入了一个包含剩余可用库存数量的记录,这使得原始记录完全保留给该订单号,即它允许我们稍后更改或取消该订单。 为什么要保存“剩余可用库存数量”?评论allenbrowne.com/AppInventory.html 其背后的业务逻辑有点复杂,可能是我解释的不好——抱歉!但我的问题纯粹是技术问题:为什么“SET NumberOfBlocks = BlocksReserved”会失败,而“SET NumberOfBlocks = 50”或 SET NumberOfBlocks = (BlocksReserved * 1)” 有效。 由于其他人似乎忽略了您已经分享的细节(即,对我来说很明显您已经检查过数据类型等),我会冒险并责怪 Access。由于它没有将其作为传递执行,因此 Access 必须在发送到 SQL Server 之前分析和重建查询。抱歉,我不熟悉 SQL Server,但我知道您应该能够观察到从 Access 传递到服务器的查询。我怀疑 SQL Server 没有收到相同的查询。这并没有解释为什么会发生这种情况,而是将原因归结为 Access。 【参考方案1】:

进一步的测试证实这是执行命令在以下情况下的错误:

这是一条 UPDATE 语句,其中一个整数字段被分配给另一个整数字段,例如 SET FieldA = FieldB(相同的数字数据类型) 它是 SQL Server 中的链接表。

同样的 SQL 语句也可以正常工作

在 Access 中使用表或 在查询中使用时。

经过测试的解决方法:

显式转换字段:SET FieldA = CInt(FieldB)...(或 CLng...) 使用任何计算:SET FieldA = FieldB * 1 使用变量:SET FieldA = " & intFieldB

【讨论】:

【参考方案2】:

这很有趣。我可以使用 Access 2010、SQL Server 2008 R2、ODBC Driver 17 for SQL Server 重现它。

如果 (N)VARCHAR 列包含在 UPDATE 查询中!UPDATE AAA SET Smallint2 = Smallint1, Int2 = Int1; 有效。

CREATE TABLE AAA (
    ID int IDENTITY(1,1) NOT NULL, 
    Smallint1 SMALLINT NULL, 
    Smallint2 SMALLINT NULL, 
    Int1 INT NULL,
    Int2 INT NULL,
    foo NVARCHAR(255) NULL,
    RV TIMESTAMP NOT NULL,

    CONSTRAINT PK_AAA PRIMARY KEY (ID)
)
GO

INSERT AAA (Smallint1, Smallint2, Int1, Int2, foo) 
VALUES (1, 0, 77, 9999, 'asdf'), 
       (3456, NULL, NULL, 1234, 'null')

访问-VBA:

Sub TestAAA()

    Dim strSql As String

    strSql = "UPDATE AAA SET Smallint2 = Smallint1, Int2 = Int1;"
    CurrentDb.Execute strSql, dbFailOnError + dbSeeChanges

    Stop
    ' Requery table => UPDATE was successful!

    ' Edit and save values in Smallint1 / Int1

    strSql = "UPDATE AAA SET Smallint2 = Smallint1, Int2 = Int1, foo = 'with NVARCHAR';"
    CurrentDb.Execute strSql, dbFailOnError + dbSeeChanges

    Stop
    ' Requery => Smallint2 / Int2 are not updated, "foo" is!

    strSql = "UPDATE AAA SET Smallint2 = CInt(Smallint1), Int2 = CLng(Int1), foo = 'with Conversion';"
    CurrentDb.Execute strSql, dbFailOnError + dbSeeChanges

    ' Requery => Smallint2 / Int2 are updated!

End Sub

结果:

初始状态 第一次更新后 手动编辑和第二次更新后 第三次更新后
+----+-----------+-----------+-----------+-----------+-----------------+
| ID | Smallint1 | Smallint2 |   Int1    |   Int2    |       foo       |
+----+-----------+-----------+-----------+-----------+-----------------+
| 1  | 1         | 0         | 77        | 9999      | asdf            |
| 2  | 3456      |           |           | 1234      | null            |
|    |           |           |           |           |                 |
| ID | Smallint1 | Smallint2 | Int1      | Int2      | foo             |
| 1  | 1         | 1         | 77        | 77        | asdf            |
| 2  | 3456      | 3456      |           |           | null            |
|    |           |           |           |           |                 |
| ID | Smallint1 | Smallint2 | Int1      | Int2      | foo             |
| 1  | 222       | 1         | 988888888 | 77        | with NVARCHAR   |
| 2  | 333       | 3456      | 999999999 |           | with NVARCHAR   |
|    |           |           |           |           |                 |
| ID | Smallint1 | Smallint2 | Int1      | Int2      | foo             |
| 1  | 222       | 222       | 988888888 | 988888888 | with Conversion |
| 2  | 333       | 333       | 999999999 | 999999999 | with Conversion |
+----+-----------+-----------+-----------+-----------+-----------------+

【讨论】:

我可以使用 SQL Server 2016 Developer 在 Access 2016 (365) 中确认您的观察结果:如果没有 nvarchar 字段作为更新的一部分,则不需要转换。

以上是关于带有 SQL Server 后端更新的 MS Access 失败且没有错误的主要内容,如果未能解决你的问题,请参考以下文章

MS Access VBA 和 SQL Server - 记录集更新时 ODBC 调用失败

SQL Server 扩展属性 MS_SubdatasheetName

SQL-Server 后端、MS Access 前端:连接

MS Access 2003 可以连接到的最新 SQL Server 后端是啥?

多个 MS Access 前端连接到单个远程 SQL Server 后端

MS Access 前端与 SQL Server 后端查询存储最佳实践 [关闭]