【问题标题】:Access VBA: Query returns no rowsAccess VBA:查询不返回任何行
【发布时间】:2012-10-01 13:33:53
【问题描述】:

我写了一些 VBA:

For x = LBound(resProdID) To UBound(resProdID)
    CurrentDb.QueryDefs("qry_findID_vba").SQL = "" & _
    "SELECT tbl_products.ProdID " & _
    "FROM tbl_products " & _
    "WHERE (tbl_products.Size = " & resSize(x) & " " & _
    "AND tbl_products.SupplID = '" & Forms.frm_suppliers.SupplID & "')"

    Dim dbs As DAO.Database
    Dim rst As DAO.Recordset

    Set dbs = CurrentDb()
    Set rst = dbs.OpenRecordset("qry_findID_vba")

    MsgBox rst.RecordCount

    If rst.RecordCount <> 0 Then
        rst.MoveLast
        rst.MoveFirst

        newProdID(x) = rst.Fields(0).Value
        MsgBox "This never fires"
     End If

    rst.Close
    Set rst = Nothing
    dbs.Close
    Set dbs = Nothing
Next x

当我运行它时,会弹出一个显示 0 的框。我单击“确定”,然后它又重复了一次。这是因为我的 resProdID 数组中有两个项目。

但是,如果我正常打开查询“qry_findID_vba”,它会像我预期的那样显示一行。

为什么 VBA 不返回这一行?我做错了什么吗?

【问题讨论】:

  • 您是否验证了 resSize 数组和 Forms.frm_suppliers.SupplID 中的值实际上与您正在搜索的表中的值一致?
  • 你的 SQL 是什么?您应该添加一个显示 SQL 的 debug.print 或 msgbox。该 SQL 是否返回一行?如果没有,很可能,那就是问题所在。
  • DAO 已经过时了。我可以建议改用 ADO 吗?
  • @DavidW 是的,它们都是整数。如您所见,我修改了一个实际查询,如果我在运行 Sub 后打开它,它会显示一行。所以我知道它在那里,VBA 就是找不到它。这是奇怪的事情,我不知道为什么..
  • @DanielCook 是的,我将 SQL 作为 InputBox 的默认值输出。然后我尝试在查询对象中运行 SQL,它按预期工作。如果您查看上面的评论,我已经验证对象“qry_findID_vba”正在工作..

标签: sql ms-access vba dao ms-access-2010


【解决方案1】:

此代码消息框的计数是否正确?你可以用它来代替吗? (注意,我实际上并没有运行它,所以请注意轻微的语法错误。)

For x = LBound(resProdID) To UBound(resProdID) 
    Dim sql as String
    Dim rst As DAO.Recordset 
    sql = "Select tbl_products.ProdID FROM tbl_products " & _
          "WHERE (tbl_products.Size = " & resSize(x) & " " & _
          "AND tbl_products.SupplID = '" & Forms.frm_suppliers.SupplID & "')" 
    Set rst = dbs.OpenRecordset(sql)
    if not rst.eof then 
        MsgBox rst.fields("ProdID")    
    else
        Msgbox "None found!"
    end if
    rst.Close 
    Set rst = Nothing 
Next x 

另外,尝试将所有内容复制到新表单,并压缩和修复数据库...

【讨论】:

  • 如果这种完全抛弃 QueryDef 方法的方法不合适,请告诉我,我将删除此答案。
  • 修复了 MsgBox 字段而不是计数。
  • 我试过只使用 SQL 而不是使用 QueryDef 方法。同样的问题..
  • SupplID 是文本还是数字?
【解决方案2】:

首先,您确实应该使用 QueryDef 参数。它们提供了许多好处:

  • 防止格式错误的输入和 SQL 注入的安全网。
  • 您无需在每次参数值更改时重新定义查询 SQL 文本。
  • 它们使您的 VBA 独立于查询文本。这是一个简单的查询,但如果您不必为了更改 SQL 而更改 VBA 代码,则更复杂的查询会受益。
  • 它们提供类型安全 - 您可以在 VBA 中使用类型化变量并确保查询不会因数据类型错误而失败。
  • 它们可以重复使用 - 例如,参数化查询可以绑定到表单或直接执行。
  • 最后但并非最不重要的一点是,在 VBA 中使用时,它看起来更漂亮、更清晰。

您的情况正是参数化 QueryDefs 的用途。

将Access中qry_findID_vba的查询文字改为:

PARAMETERS [ProductSize] Text (255), [SupplID] Number;
SELECT ProdID
FROM tbl_products
WHERE [tbl_products].[Size] = [ProductSize] AND [tbl_products].[SupplID] = [SupplID];

根据您在表中的实际数据类型替换参数数据类型。

接下来,当您处于循环中时,不要一次又一次地重新定义固定变量。 dbsrst 根本不需要在循环内定义。

最后一点,RecordCount 属性并不像您想象的那样工作。 Quote from the docs,强调我的:

使用RecordCount 属性找出一个记录中有多少条记录 Recordset 或 TableDef 对象已被访问。 记录数 属性并不表示一个包含多少条记录 dynaset-、snapshot- 或 forward-only-type 记录集对象,直到所有 记录已被访问。 [...] 要强制访问最后一条记录,请在 Recordset 对象上使用 MoveLast 方法。

您也可以检查.EOF 属性,而不是调用MoveLast。如果为 false,则至少有一条记录可用。

对于像这样的一次性查询结果,我建议使用快照类型 Recordset。您可以定义 which type you want 在 QueryDef 上调用 OpenRecordset 时使用。

现在:

Dim qry_findID_vba As DAO.QueryDef 
Set qry_findID_vba = CurrentDb().QueryDefs("qry_findID_vba")

qry_findID_vba.Parameters("SupplID") = Forms.frm_suppliers.SupplID

For x = LBound(resProdID) To UBound(resProdID)
    qry_findID_vba.Parameters("ProductSize") = resSize(x)

    With qry_findID_vba.OpenRecordset(dbOpenSnapshot)
        If Not .EOF Then 
            newProdID(x) = .Fields("ProdID").Value 
        End If
    End With
Next x

请注意,我使用With 来节省维护一个助手rst 变量。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-04
    • 2017-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多