简体   繁体   English

访问VBA:查询不返回任何行

[英]Access VBA: Query returns no rows

I've written some VBA: 我写了一些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

What happens when I run it, is that a box pops up saying 0. I click Ok, and it repeats one more time. 当我运行它时会发生什么,是弹出一个框说0。我点击确定,然后再重复一次。 This is because I have two items in my resProdID-array. 这是因为我的resProdID数组中有两个项目。

However, if I open the query "qry_findID_vba" normally, it shows one row, like I expected. 但是,如果我正常打开查询“qry_findID_vba”,它会显示一行,就像我预期的那样。

Why doesn't VBA return this row? 为什么VBA不返回这一行? Have I done anything wrong? 我做错了什么吗?

Does this code messagebox the correct count? 此代码消息框是否正确计数? Can you use it instead? 你可以用吗? (Note, I haven't actually run it, so watch out for slight syntax errors.) (注意,我实际上没有运行它,所以要注意轻微的语法错误。)

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 

Also, try copying everything to a new form, and compacting and repairing the database ... 另外,尝试将所有内容复制到新表单,并压缩和修复数据库...

First off, you really should use QueryDef parameters. 首先,你真的应该使用QueryDef参数。 They provide a number of benefits: 它们提供了许多好处:

  • A safety net against malformed input and SQL injection. 防止格式错误的输入和SQL注入的安全网。
  • You don't need to redefine the query SQL text every time a parameter value changes. 每次参数值更改时,您都不需要重新定义查询SQL文本。
  • They make your VBA independent of the query text. 它们使您的VBA独立于查询文本。 This is a simple query, but more complex ones benefit if you don't have to change your VBA code just to change the SQL. 这是一个简单的查询,但如果您不必更改VBA代码来更改SQL,则更复杂的查询会受益。
  • They provide type safety - you can use typed variables in VBA and be sure that the query cannot fail with data type errors. 它们提供类型安全性 - 您可以在VBA中使用类型变量,并确保查询不会因数据类型错误而失败。
  • They can be re-used - parameterized queries can be bound to forms, or executed directly, for example. 它们可以重复使用 - 例如,参数化查询可以绑定到表单或直接执行。
  • Last but not least, it looks much nicer and clearer when used in VBA. 最后但同样重要的是,在VBA中使用它看起来更好更清晰。

Your situation is exactly what parameterized QueryDefs are for. 您的情况正是参数化QueryDefs的用途。

Change the query text of qry_findID_vba in Access to: 在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];

Replace the parameter data types according to your actual data types in the table. 根据表中的实际数据类型替换参数数据类型。

Next, when you're in a loop, don't re-define fixed variables again and again. 接下来,当您处于循环中时,不要一次又一次地重新定义固定变量。 dbs and rst don't need to be defined inside the loop at all. dbsrst根本不需要在循环内定义。

Last point, the RecordCount property does not work the way you think. 最后一点, RecordCount属性不像您想象的那样工作。 Quote from the docs , emphasis mine: 从文档引用 ,强调我的:

Use the RecordCount property to find out how many records in a Recordset or TableDef object have been accessed. 使用RecordCount属性可以找出Recordset或TableDef对象中已访问的记录数。 The RecordCount property doesn't indicate how many records are contained in a dynaset–, snapshot–, or forward–only–type Recordset object until all records have been accessed. 在访问所有记录之前 ,RecordCount属性不指示动态集,快照或仅前向类型Recordset对象中包含的记录数。 [...] To force the last record to be accessed, use the MoveLast method on the Recordset object. [...]要强制访问最后一条记录,请使用Recordset对象上的MoveLast方法。

Instead of calling MoveLast , you can also check the .EOF property. 您也可以检查.EOF属性,而不是调用MoveLast If it is false, at least one record is available. 如果为false,则至少有一条记录可用。

For one-off query results like this one, I would recommend using the snapshot type Recordset. 对于像这样的一次性查询结果,我建议使用快照类型Recordset。 You can define which type you want to use when you call OpenRecordset on the QueryDef. 您可以在QueryDef上调用OpenRecordset时定义使用的类型

Now: 现在:

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

Note that I use With to save maintaining a helper rst variable. 请注意,我使用With来保存辅助器rst变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM