【问题标题】:VBA: Interact with Access from within OutlookVBA:从 Outlook 中与 Access 交互
【发布时间】:2018-11-15 15:00:54
【问题描述】:

我正在尝试在 Outlook 中创建一些与 Access 数据库中包含的表交互的自定义​​按钮。到目前为止,我的按钮在 Outlook 中工作,运行代码来实例化自定义数据访问类,该类依次处理打开和关闭与数据库的连接。据我所知,这很有效。

但是,我什至无法从此类中执行简单的选择查询。谁能帮我理解为什么下面的代码可能不起作用?我总是以一个没有行的记录集结束,但如果我使用 Access 查询设计器运行相同的 sql,它就可以正常工作。

Public Function GetJobID(ByVal xEmailID As String) As Integer
'Returns the JobID associated with a given EmailID from the email link table.
'Returns a fail constant if no link exists.
    Dim rs As ADODB.Recordset
    Dim sql As String

    'Exit if not connected.
    'Cast to boolean because VBA doesn't recognise connection state integer as boolean.
    If Not CBool(mConn.State) Then
        GetJobID = RESULT_FAIL_INTEGER
        Exit Function
    End If

    sql = "SELECT [JobID] FROM [EMAIL_LINK_TABLE] WHERE [EmailID]='xEmailID'"
    sql = Replace(sql, "EMAIL_LINK_TABLE", EMAIL_LINK_TABLE)
    sql = Replace(sql, "xEmailID", xEmailID)

    On Error Resume Next
    Set rs = mConn.Execute(sql)

    If rs.RecordCount > 0 Then
        GetJobID = rs(1).Value
    Else
        GetJobID = RESULT_FAIL_INTEGER
    End If

End Function

【问题讨论】:

  • EmailID 是字符串吗?我不会在参数上使用Replace - 只需使用an actual parameterized query...
  • 电子邮件 ID 作为字符串存储在 Access 中。在on error resume next 处设置断点并在即时窗口中调用?sql 会产生一个SQL 字符串,在Access 查询设计器中执行该字符串时会返回正确的结果。我意识到参数化查询会更好,但我不喜欢它们弄乱全局命名空间,并且不想一直创建和删除它们。我发现只需写出 SQL 字符串并替换占位符文本即可轻松阅读和维护。在这里,安全性和效率都不重要。
  • 弄乱了全局命名空间?我不确定我知道你的意思。该参数可以是本地的。 注释掉 On Error Resume Next 看看是否有错误。
  • 注释掉的那一行没有发生错误。我只是得到一个没有行的记录集。我对参数化查询的经验是它们与数据库一起保存,我不知道它们可能是本地的。如果它们需要比我的方法更多的代码行,我无论如何都会避免使用它们,因为我无法在 VBA 中折叠代码,而且当文件中有太多内容时,我发现很难阅读。
  • 问题似乎出在rs.RecordCount,它总是返回-1。如果我用if Not rs.EOF 替换if rs.RecordCount > 0,那么确实有返回的行,我可以通过它的索引访问正确的数据(应该是0 而不是1)。所以代码有效,但rs.RecordCount 无效,即使我使用rs.MoveLast 后跟rs.MoveFirst。谁能解释这是为什么?

标签: vba ms-access outlook


【解决方案1】:

我看到您已将问题追踪到 .RecordCount 返回 -1。

这是动态游标的标准行为,来自the docs

Recordset 对象的游标类型影响是否可以确定记录数。对于只进游标,RecordCount 属性将返回 -1;静态或键集游标的实际计数;以及 -1 或动态游标的实际计数,具体取决于数据源。

当然,您可以修改代码以使用静态游标,但这会影响性能。相反,要测试您的记录集中是否有记录,请使用.EOF(一种返回布尔值以指示记录集当前是否位于文件末尾的方法)。当只需要加载第一个记录时,这将使您的代码不必加载所有记录:

Public Function GetJobID(ByVal xEmailID As String) As Integer
'Returns the JobID associated with a given EmailID from the email link table.
'Returns a fail constant if no link exists.
    Dim rs As ADODB.Recordset
    Dim sql As String

    'Exit if not connected.
    'Cast to boolean because VBA doesn't recognise connection state integer as boolean.
    If Not CBool(mConn.State) Then
        GetJobID = RESULT_FAIL_INTEGER
        Exit Function
    End If

    sql = "SELECT [JobID] FROM [EMAIL_LINK_TABLE] WHERE [EmailID]='xEmailID'"
    sql = Replace(sql, "EMAIL_LINK_TABLE", EMAIL_LINK_TABLE)
    sql = Replace(sql, "xEmailID", xEmailID)

    On Error Resume Next 
    Set rs = mConn.Execute(sql)

    If Not rs.EOF Then
        GetJobID = rs(0).Value
    Else
        GetJobID = RESULT_FAIL_INTEGER
    End If

End Function

【讨论】:

  • 感谢您的反馈。我很欣赏为什么.RecordCount 是-1 的信息,尽管我不喜欢 VBA 的错误处理方法,因为语法对我来说很讨厌,而且没有代码折叠,我更喜欢利用一切机会减少代码行。当错误处理很重要时,我更喜欢测试错误编号,然后返回失败常量和消息。在这种情况下,它并不重要,因此获胜的行数更少。如果您愿意将 rs(1).Value 修改为 rs(0).Value 并完全删除 On Error Resume Next 行,我会将您的回复标记为答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-24
  • 2017-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-13
相关资源
最近更新 更多