【问题标题】:Error on a recordset, but same SQL works elsewhere记录集上的错误,但相同的 SQL 在其他地方工作
【发布时间】:2014-12-15 04:47:42
【问题描述】:

错误:“运行时错误 '3061' 参数太少。预期为 2。

我编写了这个简单的函数,它返回为更改的记录数计算的剩余百分比。它应该在用户更新名为“百分比”的字段时发生。我确信下面的代码应该可以工作,但显然有问题。发生就行了:

Set rs = db.OpenRecordset("SELECT Tier1, [Percentage], Tier3 AS Battalion, Month " _
    & "FROM tbl_CustomPercent " _
    & "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND ((Month)=[Forms]![frmEntry]![cmbMonth]));", dbOpenSnapshot)

我想知道当使用“百分比”文本框填充表单的“记录源”时,它怎么会失败。

Function RemainingPercentAvailable() As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim strSQL As String


Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT Tier1, [Percentage], Tier3 AS Battalion, Month " _
& "FROM tbl_CustomPercent " _
& "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND ((Month)=[Forms]![frmEntry]![cmbMonth]));", dbOpenSnapshot)

Dim CurrentTotal As Single

CurrentTotal = 0

If Not (rs.EOF And rs.BOF) Then
    rs.MoveFirst

    Do Until rs.EOF = True
        CurrentTotal = CurrentTotal + rs!Percentage
        rs.MoveNext
    Loop

End If


RemainingPercentAvailable = "Remaing available: " & Format(1 - CurrentTotal, "0.000%")

Set rs = Nothing
Set db = Nothing

End Function

【问题讨论】:

    标签: ms-access vba recordset


    【解决方案1】:

    调整您的代码以使用带有 QueryDef 的 SELECT 语句,提供参数值,然后从 QueryDef 打开记录集。

    Dim db As DAO.Database
    Dim qdf As DAO.QueryDef
    Dim rs As DAO.Recordset
    Dim strSQL As String
    
    strSQL = "SELECT Tier1, [Percentage], Tier3 AS Battalion, [Month] " _
        & "FROM tbl_CustomPercent " _
        & "WHERE (((Tier1)=[Forms]![frmEntry]![cmbImport_T1]) AND (([Month])=[Forms]![frmEntry]![cmbMonth]));"
    
    Set db = CurrentDb
    Set qdf = db.CreateQueryDef(vbNullString, strSQL )
    ' supply values for the 2 parameters ...
    qdf.Parameters(0).Value = Eval(qdf.Parameters(0).Name)
    qdf.Parameters(1).Value = Eval(qdf.Parameters(1).Name)
    Set rs = qdf.OpenRecordset
    

    注意:Month 是保留字。尽管该名称之前显然没有引起任何问题,但我将其括在方括号中,因此 db 引擎不会将字段名称与 Month 函数混淆。这可能是一种不必要的预防措施,但很难准确预测保留字何时会产生问题。实际上,如果可能的话,最好完全避免它们。

    【讨论】:

    • 确实有效。谢谢你。我想知道为什么我有其他实例……偶然?
    • 对于那些有效的实例,您是否直接将查询作为 DAO.Recordset 打开?
    【解决方案2】:

    这是直接在 DAO.Recordset 中调用查询,它工作得很好。
    注意同样的 'Set rs = db.OpenRecordset(strSQL, dbOpenDynaset) 这也是一个参数 SQL。 唯一的区别是,我不需要遍历和分析记录集 - 但错误发生在 'Set rs = " 行,所以我无法进一步了解。

    Dim rs As DAO.Recordset 暗淡 db 作为 DAO.Database 将 strSQL 调暗为字符串

    strSQL = "SELECT Sum(tbl_SP.AFP) AS AFP_TOTAL, tbl_SP.T1_UNIT" _ &“来自 tbl_SP”_ &“按 tbl_SP.T1_UNIT 分组”_ & "HAVING (((tbl_SP.T1_UNIT)='" & strUnit & "'));"

    设置 db = CurrentDb 设置 rs = db.OpenRecordset(strSQL, dbOpenDynaset)

    AFP_Total = rs!AFP_Total

    【讨论】:

      【解决方案3】:

      还有一种更简单的方法可以计算总百分比。

      您可以使用 DSum() 函数,而不是循环遍历记录。

      注意,如果没有记录,DSum 会返回 Null,因此需要将其包装在 Nz() 中。

      只是为了好玩,这是你的函数,但写成一个单一的语句:

          Function RemainingPercentAvailable() As String
          RemainingPercentAvailable = "Remaining available: " & Format(1 - _
              Nz(DSum("Percentage", _
                      "tbl_CustomPercent", _
                      "Tier1 = " & QString(cmbImport_T1) & _
                      " AND [Month] = " & QString(cmbMonth))) _
              , "0.000%")
          End Function
      

      【讨论】:

        【解决方案4】:

        我不建议在 VBA 中构建临时参数化查询,因为它会使代码过于复杂。而且更慢。我更喜欢构建“纯”SQL,它将直接在数据库引擎中运行,而不需要对 Access 进行任何回调。我假设您的函数是在 frmEntry 表单中定义的,并且 cmbImport_T1cmbMonth 是字符串字段。如果它们是数字,则省略 qString()

        这是我的函数版本。它正确处理空记录集的情况。

        Function RemainingPercentAvailable() As String
        Dim CurrentTotal As Double, q As String
        q = "SELECT Percentage" & _
            " FROM tbl_CustomPercent" & _
            " WHERE Tier1 = " & QString(cmbImport_T1) & _
            " AND [Month] = " & QString(cmbMonth)
        CurrentTotal = 0
        With CurrentDb.OpenRecordset(q)
          While Not .EOF
            CurrentTotal = CurrentTotal + .Fields("Percentage")
            .MoveNext
          Wend
        End With
        RemainingPercentAvailable = "Remaining available: " & _
                                    Format(1 - CurrentTotal, "0.000%")
        End Function
        
        ' Return string S quoted, with quotes escaped, for building SQL.
        Public Function QString(ByVal S As String) As String
           QString = "'" & Replace(S, "'", "''") & "'"
        End Function
        

        【讨论】:

        • 感谢汤姆解决了这些问题,即使它们不是这个问题的标题。该操作运行良好。我打算在别处询问第二件,但既然你回答了,我似乎无法弄清楚为什么计算会减少 15%。尝试对百分比值为 .25、.1.、.1、.2、.2 和 .15 的 6 条记录执行此操作。它返回“剩余可用:15.000%” - 无法想象为什么 - 显然错过了其他东西。
        • 可能“更喜欢构建将直接在 db 引擎中运行的‘纯’SQL”,但一揽子建议使用动态 SQL 而非参数化查询只是一个糟糕的建议.您声称参数化查询使代码“更复杂”(比使用 VBA 函数清理和格式化参数的可怕字符串连接?)和“更慢”(真的吗?显着可证明 更慢?)同时忽略了 (1) 防止 SQL 注入的优点,(2) 避免文字分隔符错误,(3) 不必担心 dd/mmmm/dd 日期格式。
        • 蒙蒂 - 它对我有用!不知何故,您没有将最终数字添加到总数中。尝试在 .MoveNext 之前添加 debug.print .Fields("Percentage") 并查看输出是否为 0.15。如果您现在无法复制问题,那么您的表单可能有问题。如果您重新计算表单上 txtPercentage_AfterUpdate 事件中剩余的百分比,则新值不会写入数据库。尝试在事件过程开始时添加Me.Dirty = False
        • Tom,我使用了您的第一个“DSUM”示例。我想,知道计算的表格是“子表格”会有所帮助。父表格上提供了月份和第 1 层。我在子表单旁边的父表单上的标签上写了“剩余百分比”。
        • 这是因为子窗体滞后。首先,我添加了“Me.Dirty = false”,然后当例程结束时,我进行重新查询。我尝试保存子表单打开的记录,以便我可以返回到它,但它会在每次输入后不断跳回顶部。当我找到 Me.Recalc 时,我几乎陷入了写一些东西来重新定位光标的困境——真是救命稻草。谢谢大家。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-27
        相关资源
        最近更新 更多