【问题标题】:MS Access: How to bypass/suppress an error?MS Access:如何绕过/抑制错误?
【发布时间】:2008-11-10 08:12:01
【问题描述】:

我正在执行这样的查询

select field from table;

在该查询中,有一个循环在许多表上运行。因此,如果表中不存在该字段,我会得到一个

运行时错误 3061

我怎样才能绕过这个错误,比如这个错误流上的那个应该去另一个点?

这是我最近浏览这个论坛后得到的代码。

Option Explicit

Private Sub UpdateNulls()
 Dim rs2 As DAO.Recordset
  Dim tdf As DAO.TableDef
  Dim db As Database
  Dim varii As Variant, strField As String
  Dim strsql As String, strsql2 As String, strsql3 As String
  Dim astrFields As Variant
  Dim intIx As Integer
  Dim field As Variant
  Dim astrvalidcodes As Variant
  Dim found As Boolean
  Dim v As Variant


  Open "C:\Documents and Settings\Desktop\testfile.txt" For Input As #1
  varii = ""
  Do While Not EOF(1)
    Line Input #1, strField
    varii = varii & "," & strField
  Loop
  Close #1
  astrFields = Split(varii, ",")  'Element 0 empty




        For intIx = 1 To UBound(astrFields)


        'Function ListFieldDescriptions()
                            Dim cn As New ADODB.Connection, cn2 As New ADODB.Connection
                            Dim rs As ADODB.Recordset, rs3 As ADODB.Recordset
                            Dim connString As String
                            Dim SelectFieldName

                            Set cn = CurrentProject.Connection

                            SelectFieldName = astrFields(intIx)

                            Set rs = cn.OpenSchema(adSchemaColumns, Array(Empty, Empty, Empty, SelectFieldName))

                           'Show the tables that have been selected '
                            While Not rs.EOF

                           'Exclude MS system tables '
                            If Left(rs!Table_Name, 4) <> "MSys" Then
                            strsql = "Select t.* From [" & rs!Table_Name & "] t Inner Join 01UMWELT On t.fall = [01UMWELT].fall Where [01UMWELT].Status = 4"
                            End If

                            Set rs3 = CurrentDb.OpenRecordset(strsql)

            'End Function

            strsql2 = "SELECT label.validcode FROM variablen s INNER JOIN label ON s.id=label.variablenid WHERE varname='" & astrFields(intIx) & "'"

            Set db = OpenDatabase("C:\Documents and Settings\Desktop\Codebook.mdb")
            Set rs2 = db.OpenRecordset(strsql2)

                With rs2
                .MoveLast
                .MoveFirst
                 astrvalidcodes = rs2.GetRows(.RecordCount)
                .Close '
                End With


                    With rs3
                    .MoveFirst
                    While Not rs3.EOF
                        found = False
                        For Each v In astrvalidcodes
                        If v = .Fields(0) Then
                        found = True
                        Debug.Print .Fields(0)
                        Debug.Print .Fields(1)


              Exit For
                    End If
                    Next
                If Not found Then
                msgbox "xxxxxxxxxxxxxxxx"

                End If
                End If
                .MoveNext


                Wend
                End With

           On Error GoTo 0        'End of special handling

    Wend



Next intIx


  End Sub

我来了

类型不匹配运行时错误

Set rs3 = CurrentDb.OpenRecordset(strsql)

我猜我混淆了adodao,但我不确定它在哪里。

【问题讨论】:

  • 首先:停止使用 GoTo。立即地。这不好。我的意思是真的邪恶,就像在“潘多拉的盒子”中一样。 GoTo 在 VB 代码中唯一有效的地方是“On Error Goto”语句。
  • 第二:改成“On Error Resume Next”样式,这里更合适。您是否仔细检查过 3061 是错误的实际编号?使用调试器单步调试您的代码。
  • 我不情愿地编码了 goto 但我已将其删除 我尝试调试但仍然出现相同的错误 3061
  • 我将更改我的答案以包含应该有效的内容。
  • 我在您的代码中看不到一个“On Error Resume Next”。这就是为什么它不起作用。除此之外 - 没有冒犯,但你的代码是一个混乱。你可能想先把它清理干净。我猜你也缺乏关于一般 VB 和特别是 Access VBA 的阅读。

标签: ms-access vba error-handling


【解决方案1】:

使用 VBA 提供的 On Error 语句:

Sub TableTest
  On Error Goto TableTest_Error

  ' ...code that can fail... '

  Exit Sub

:TableTest_Error
  If Err.Number = 3061 Then
    Err.Clear()
    DoSomething()
  Else
    MsgBox Err.Description ' or whatever you find appropriate '
  End If
End Sub

或者,您可以逐行关闭自动错误处理(例如中断执行并显示错误消息):

Sub TableTest
  ' ... fail-safe code ... '

  On Error Resume Next
  ' ...code that can fail... '
  If Err.Number = 3061 Then
    Err.Clear()
    DoSomething()
  Else
    MsgBox Err.Description
  End If
  On Error Goto 0

  ' ...mode fail-safe code... '
End Sub

有这些语句可用:

  • On Error Resume Next 完全关闭 VBA 集成的错误处理(消息框等),只在下一行继续执行。请务必在使用后尽早检查错误,因为悬空错误会破坏正常的执行流程。捕获错误后立即清除它以防止发生这种情况。
  • On Error Goto &lt;Jump Label&gt; 在给定标签处恢复执行,主要用于捕获各种错误的每个函数的错误处理程序。
  • On Error Goto &lt;Line Number&gt; 在给定的行号处恢复。远离它,它没有用,甚至很危险。
  • On Error Goto 0 是近亲。恢复 VBA 集成错误管理(消息框等)

编辑

根据编辑后的问题,这是我解决您问题的建议。

For Each FieldName In FieldNames ' assuming you have some looping construct here '

  strsql3 = "SELECT " & FieldName & " FROM table"

  On Error Resume Next
  Set rs3 = CurrentDb.OpenRecordset(strsql3)

  If Err.Number = 3061 Then
    ' Do nothing. We dont care about this error '
    Err.Clear
  Else
    MsgBox "Uncaught error number " & Err.Number & " (" & Err.Description & ")"
    Err.Clear
  End If

  On Error GoTo 0

Next FieldName

请务必清除错误在任何情况下,然后再继续在同一个 Sub 或 Function 中循环。正如我所说,悬空错误会导致代码流变得意外!

【讨论】:

  • 好吧,我添加了错误恢复后失败的代码,并尝试使用 If Err.Number = 3061 Then Err.Clear() 捕获它,但仍然弹出错误
  • 在您的情况下失败的代码是什么?你能编辑你的问题并包括你到目前为止的内容吗?
  • “弹出”是什么意思?在调试器中,当您逐行单步执行代码时:在“OpenRecordset(...)”之后它是否在下一行继续,而是立即显示错误?!
  • 如果你的代码没问题,那么这是不可能的。您能否再次编辑您的答案并显示您当前使用的内容。理想情况下有足够的上下文(外循环等),所以我可以看到错误在哪里。
  • 我投了反对票,因为第二种选择,使用 On Error Resume Next 跳过错误而不使用适当的错误处理程序。在我看来,这只是不好的做法。 On Error Resume Next 是危险的并且在逻辑上存在问题,应该非常谨慎地使用。
【解决方案2】:

为什么不使用 TableDefs 来检查字段或混合使用 ADO 和 DAO,而不是捕获错误? ADO Schemas 可以提供包含必填字段的表列表:

Function ListTablesContainingField()
Dim cn As New ADODB.Connection, cn2 As New ADODB.Connection
Dim rs As ADODB.Recordset, rs2 As ADODB.Recordset
Dim connString As String
Dim SelectFieldName

    Set cn = CurrentProject.Connection

    SelectFieldName = "Fall" 'For tksy '

    'Get names of all tables that have a column called 'ID' '
    Set rs = cn.OpenSchema(adSchemaColumns, _
    Array(Empty, Empty, Empty, SelectFieldName))

    'Show the tables that have been selected '
    While Not rs.EOF

        'Exclude MS system tables '
        If Left(rs!Table_Name, 4) <> "MSys" Then
            ' Edit for tksy, who is using more than one forum '
            If tdf.Name = "01UMWELT" Then
                strSQL = "Select * From 01UMWELT Where Status = 5"
            Else
                strSQL = "Select a.* From [" & rs!Table_Name _
                & "] a Inner Join 01UMWELT On a.fall = 01UMWELT.fall " _
                & "Where 01UMWELT.Status = 5"
            End If
            Set rs2 = CurrentDb.OpenRecordset(strSQL)

            Do While Not rs2.EOF
                For i = 0 To rs2.Fields.Count - 1
                    If IsNull(rs2.Fields(i)) Then
                        rs2.Edit
                        rs2.Fields(i) = 111111
                        rs2.Update
                    End If
                Next
                rs2.MoveNext
             Loop
        End If
        rs.MoveNext
    Wend
    rs.Close
    Set cn = Nothing

End Function

【讨论】:

  • 所以在这里我将获取记录集中表的名称,之后我是否必须单独查询这些表?如何查询只在记录集中列出的表?
  • 您可以从记录集 (wiki.lessthandot.com/index.php/ADO_Schemas) 构建一个列表或表数组,或者在 While 循环中插入额外的编码。
  • 我不确定我是否理解正确。现在在记录集中我是只获取表名还是获取包含内容的表
  • 试一下代码是安全的。它将返回包含您选择的字段的表名列表。
  • 我添加了您的代码,然后使用了这样的 for 循环 For Each tdf In CurrentDb.TableDefs If tdf.Name = rs!Table_Name Then 但我认为 if 语句不起作用我收到运行时错误-2147217908(80040e0c)
【解决方案3】:

试试这个:

On Error Resume Next ' 如果发生错误,移至下一条语句。

...尝试选择的语句...

如果 (Err 0) 那么

...act on error, or simply ignore if necessary...

如果结束

On Error Goto 0 ' 将错误处理重置为之前的状态。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多