【问题标题】:Handling AddNew Key Violations ADODB.Recordset处理 AddNew 密钥违规 ADODB.Recordset
【发布时间】:2020-07-13 21:55:31
【问题描述】:

我以前的标题在那里引起了一些混乱...更新 我最近在使用一些 VBA 连接 Excel 和 Access 时遇到了这个问题。

所以我有一个电子表格,其中包含一个需要导入 Access 数据库的表。 该表是通用的,如下所示。

EmployeeNumber  Unused_Field2   Unused_Field3
1                    @@@              @@@
2                    @@@              @@@
3                    @@@              @@@

Access 中的唯一键设置为 EmployeeNumber。

我在 Excel 中的 VBA 代码如下所示:

Sub test()
    Dim con As ADODB.Connection
    Dim rst As ADODB.Recordset

    strcon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\temp\mydb.mdb;"
    strsql = "SELECT * FROM Table1"

    Set con = New Connection
    Set rst = New Recordset

    con.Open strcon
    rst.Open strsql, strcon, adOpenStatic, adLockOptimistic

    For i = 0 To n
        On Error GoTo Errhdl
        rst.AddNew Array("Field1", "Field2", "Field3"), Array(Range("A" & i), Range("B" & i), Range("C" & i))
        On Error GoTo 0
    Next

    Exit Sub

Errhdl:

    Debug.Print "Record" & i & "caused an error"
    Resume Next

End Sub

不幸的是,数据质量不高,我经常会有重复的值导致密钥违规。 虽然我虽然 Resume Next 将清除允许另一个“AddNew”运行的错误,但它没有。 一个键违规后的所有后续条目都将返回相同的错误。

所以我的问题如下:

  1. 是否可以清除与记录集相关的错误? (作为记录,我试图获取“错误集合”并使用 方法 Errors.Clear。那没有成功。错误集合是特定于 ADO 对象的属性 - MSDN Errors Collection)
  2. 可以在不关闭并重新打开记录集的情况下完成此操作吗?

如果需要更多说明,请告诉我!

【问题讨论】:

  • 我会先将您的标签重命名为 Err 以外的名称,这已经是 VBA 中的一个对象。
  • 你不能用Command代替Recordset来做INSERTs吗?
  • @shahkalpesh 是的,command.execute 方法可以与 resumenext 一起使用,但是与记录集相比,它提供的控制更少。另外,这意味着我需要一个字符串构造函数来构造 SQL 语句,该语句有时容易出错,尤其是在使用不同的数据类型和特殊字符时。 (反正这是我的看法)
  • @DerekCheng:不。我认为这不容易出错。 CommandParameters 集合一起使用是一种简单、无错误的方法,无需进行字符串连接。 Here 就是一个例子。
  • +1 是的,参数(一如既往)让构建 sql 变得痛苦。

标签: excel ms-access vba ado


【解决方案1】:

我不会使用错误处理(处理 ADO 错误很棘手),而是使用Find 在每次执行插入之前检查密钥是否不存在。如果键是索引字段(如您的描述所示),那么您可能会考虑Seek,这对于大型数据集更有效。 (但是,Seek 不适用于客户端光标 -adUseClient。)

这是我发现的一个代码片段,其中概述了步骤:

If Not .EOF Then
   .MoveFirst
   .Find "TPItemNbr='" & m_TPItemNbr & "'", , adSearchForward
End If
If .EOF Then
   .AddNew
   !TPItemVendorID = m_TPItemVendorID
   !TPItemNbr = m_TPItemNbr
   !TPItemEUOM = m_TPItemEUOM
   !TPItemUOMFactor = m_TPItemUOMFactor
   !TPItemPUOM = m_TPItemPUOM
   !TPItemDescription = m_TPItemDescription
   !TPItemUnitCost = m_TPItemUnitCost
   !TPItemUnitLabor = m_TPItemUnitLabor
   .Update

也就是说,每次在循环中执行一次查找,如果 EOF(文件结尾)为真,则键不在表中,因此可以执行插入。

添加以响应有关复合键的更多信息。 我会创建一个Command 对象并使用Execute。是的,它需要创建一个字符串,但您可以捕获并忽略键违规的错误。

替代方案可能是

  • 运行单独的 SQL 语句以获取重复列表的记录集
  • 循环遍历将值存储在一个数组中
  • 每次检查数组时都执行AddNew

这对我来说似乎很乱,尤其是搜索数组方面。

  • 追求评论中链接的多查找方法。

【讨论】:

  • 就个人而言,我不会使用 Array() 方法,我只会设置各个字段。 IMO 使用 Array 增加了一些复杂性并且更容易出错。但这只是我的看法;)
  • 首先感谢您的回答。我考虑过在 AddNew 之前进行搜索。在性能方面,如果我的表大小由很多行组成,它会大大减慢过程吗?
  • 另外,如果表有一个复合键(我知道我们不应该使用复合键......但它不取决于我......)然后 .Find 将不起作用(纠正我如果我错了)我将不得不使用微软建议的这个多重查找功能 - support.microsoft.com/kb/195222
【解决方案2】:

因此,如果 Recordset.AddNew 遇到错误,我找到了解决方案。 诀窍是使用 CancelUpdate - MSDN

还可以使用 Status 属性来检查操作是否成功。

示例代码如下:

Sub test()
    Dim con As ADODB.Connection
    Dim rst As ADODB.Recordset

    strcon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\temp\mydb.mdb;"
    strsql = "SELECT * FROM Table1"

    Set con = New Connection
    Set rst = New Recordset

    con.Open strcon
    rst.Open strsql, strcon, adOpenStatic, adLockOptimistic

    For i = 0 To n
        On Error GoTo Errhdl
        rst.AddNew Array("Field1", "Field2", "Field3"), Array(Range("A" & i), Range("B" & i), Range("C" & i))
        On Error GoTo 0
    Next

    Exit Sub

Errhdl:

    Debug.Print "Record" & i & "caused an error"
    If rst.Status <> 0 Then
        rst.CancelUpdate
    End If
    Resume Next

End Sub

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-22
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    相关资源
    最近更新 更多