VB.NET通过ODBC连接DB2时ExecuteNonQuery返回0未更新数据问题
VB.NET ODBC连接DB2更新不生效问题排查
问题现象
- 遗留VB.NET应用通过ODBC连接IBM DB2写入数据,最后一条更新语句执行异常
- 已排除参数多余空格、数据类型不匹配问题,确认传入的String类型参数取值正确
- 执行
Command.ExecuteNonQuery()后返回值始终为0,无异常抛出,数据库无实际更新,但代码会弹出写入成功提示 - 手动在数据库端执行对应更新SQL可正常更新数据,断点调试时出现报错:
Overload resolution failed because no accessible 'Item' accepts this number of arguments
(重载解析失败,不存在可访问的、匹配当前参数个数的“Item”方法)
问题涉及的核心代码如下:
Private Sub Merge(ByVal connectionString As String, ByVal CaseNo As String, ByVal CancelledSurgeryId As String, ByVal ActiveSurgeryId As String, ByVal Cancelled As String, ByVal Active As String, ByVal location As String) Using connection As OdbcConnection = New OdbcConnection(connectionString) Dim Command As New OdbcCommand() Dim transaction As OdbcTransaction Command.Connection = connection Dim a, b, c, d, e, f, g, h As Integer Try connection.Open() transaction = connection.BeginTransaction() Command.Connection = connection Command.Transaction = transaction Command.CommandText = "Update QS36F.ORPATSURG SET REQSTAT = 'F' WHERE CASENO = ? AND SURGERYID = ?" Command.Parameters.Add("@CASENO", Odbc.OdbcType.VarChar).Value = CaseNo Command.Parameters.Add("@SURGERID", Odbc.OdbcType.VarChar).Value = CancelledSurgeryId a = Command.ExecuteNonQuery() Command.CommandText = "delete FROM QS36F.orpctrx WHERE orderno = ? AND CASENO = ? " & _ "AND oper IN (SELECT oper FROM QS36F.orpctrx WHERE caseno = ? AND orderno = ?)" Command.Parameters.Add("@orderno", Odbc.OdbcType.VarChar).Value = CancelledSurgeryId Command.Parameters.Add("@CASENO", Odbc.OdbcType.VarChar).Value = CaseNo Command.Parameters.Add("@caseno", Odbc.OdbcType.VarChar).Value = CaseNo Command.Parameters.Add("@orderno", Odbc.OdbcType.VarChar).Value = ActiveSurgeryId b = Command.ExecuteNonQuery() Command.CommandText = "DELETE FROM QS36F.ORPCTRXM WHERE CASENO = ? AND ORDERNO = ?" Command.Parameters.Add("@caseno", Odbc.OdbcType.VarChar).Value = CaseNo Command.Parameters.Add("@orderno", Odbc.OdbcType.VarChar).Value = ActiveSurgeryId d = Command.ExecuteNonQuery() Command.CommandText = "update libor.invtrx set referno = ? , pono = ? where referno = ? and pono = ?" Command.Parameters.Add("referno", Odbc.OdbcType.VarChar).Value = Active Command.Parameters.Add("pono", Odbc.OdbcType.VarChar).Value = ActiveSurgeryId Command.Parameters.Add("referno", Odbc.OdbcType.VarChar).Value = Cancelled Command.Parameters.Add("pono", Odbc.OdbcType.VarChar).Value = CancelledSurgeryId h = Command.ExecuteNonQuery() transaction.Commit() MessageBox.Show("Records successfully written to database.", "Success") Catch ex As Exception MessageBox.Show(ex.Message) Try transaction.Rollback() Catch End Try End Try End Using End Sub
最后一条更新语句对应的样例表数据:
| ITEMID | TRXDATE | TRXTYPE | REFERNO | PONO | CASENO |
|---|---|---|---|---|---|
| 00244 | 220609 | 13 | 31215380077 | 196474 | 01631892 |
根因分析
ODBC驱动的参数绑定规则是按位置顺序匹配,不识别参数名,代码中复用同一个OdbcCommand对象执行多条SQL时,从未清空过Parameters集合,每执行一条语句就会往集合里累加新参数:
- 执行第一条Update后,参数集合有2个参数
- 执行第二条Delete后,参数集合累加4个,总计6个参数
- 执行第三条Delete后,参数集合累加2个,总计8个参数
- 执行最后一条Update时,SQL只需要4个参数,但参数集合里已经堆了12个参数,驱动会按顺序取集合里最靠前的4个参数(也就是第一条语句的CaseNo、CancelledSurgeryId,加上第二条语句的CancelledSurgeryId、CaseNo)作为查询条件,和实际需要的参数值完全不匹配,自然匹配不到任何行,返回更新行数0,也不会抛出语法/类型错误。
断点调试时出现的Item重载报错,就是因为参数集合里存在大量重名参数,按名称索引时无法匹配到唯一项导致的。
修复方案
- 每次为Command赋值新的
CommandText前,必须调用Command.Parameters.Clear()清空历史参数,再严格按照当前SQL语句中?占位符的出现顺序,依次添加对应参数,不要依赖参数名做匹配。 - 最后一条更新语句的修正代码示例:
' 清空之前累积的所有参数 Command.Parameters.Clear() Command.CommandText = "update libor.invtrx set referno = ? , pono = ? where referno = ? and pono = ?" ' 严格按?的顺序传参,参数名仅做开发时标识用,不影响绑定逻辑 Command.Parameters.Add("@new_referno", Odbc.OdbcType.VarChar).Value = Active Command.Parameters.Add("@new_pono", Odbc.OdbcType.VarChar).Value = ActiveSurgeryId Command.Parameters.Add("@old_referno", Odbc.OdbcType.VarChar).Value = Cancelled Command.Parameters.Add("@old_pono", Odbc.OdbcType.VarChar).Value = CancelledSurgeryId h = Command.ExecuteNonQuery() ' 增加返回值校验,更新行数不符合预期时直接回滚,不要弹成功提示 If h <> 1 Then Throw New Exception($"invtrx表更新异常,预期更新1行,实际更新{h}行") End If
- 长期优化建议:不要复用同一个OdbcCommand对象执行多条SQL,每执行一条语句新建独立的Command实例,从根源避免参数串用问题;事务提交前校验所有语句的ExecuteNonQuery返回值,只要有一个不符合预期就触发回滚,避免出现数据部分更新、误弹成功提示的问题。
内容的提问来源于stack exchange,提问作者Joe
相关产品推荐
相关产品推荐

