【问题标题】:DAO passthrough query using VBA: "Error 3131 Syntax error in from clause"VBA DAO 直通查询意外行为
【发布时间】:2022-01-24 00:50:38
【问题描述】:

我刚刚复制粘贴在 stackoverflow 中找到的解决方案,做了一些调整以适应我的需要,但......它不起作用:| 代码如下:

Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
    Dim strQuery As String
    Dim qdf As DAO.QueryDef
    Dim rst As DAO.Recordset
    
    If IsMissing(personId) Then
        strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
    Else
        strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
    End If
        
    Set qdf = CurrentDb.CreateQueryDef("")
    With qdf
        .SQL = strQuery
        .Connect = getDBConnectionString
        .ReturnsRecords = True
    End With
    
    Set rst = qdf.OpenRecordset
    Debug.Print rst!qryTest
    Set getAssortmentTypes = rst
End Function

在我的 postgresql 数据库中,我确实有工作功能和适当的表。我已经用 DBEaver 测试了 sql 查询并且它们有效。当我调用没有参数的函数时,我只收到一行(应该是大约 30 行)。使用参数,我期望过滤结果集,但收到“来自子句中的错误 3131 语法错误”。 有人可以帮我吗? TY

【问题讨论】:

  • 您正在使用 DAO 从 MS Access 的 VBA 宏中查询 PostgreSQL 数据库?
  • (" & personId & ")"
  • sql 注入问题仅适用于用户输入——如果 personID 是用户输入,那么建议很好——但我们没有理由怀疑甚至暗示 personID 是用户输入的结果。很可能 personID 是来自其他代码的 PK 记录 ID - 不是基于用户输入,而是基于记录的 PK 行 - 因此该代码很好。
  • 顺便说一下,使用DAO(即ODBC)是推荐使用的技术堆栈。 OLEdb 是一种仅限 Windows 的技术——而 ODBC 和 DAO 是前进的道路,因为其他平台不再支持 ADO。事实上,即使是微软 10 年前也推荐过这个。然而,在 2018 年,他们推翻了这一决定?使用 ODBC 堆栈是推荐的选择 - 因为这是一个行业标准 - 而 ADO 不是。 (不要与 ado.net 混淆)。
  • @Dai - 是的,你是对的。好吧,MS Access 根本不是安全重点,正如 Albert 提到的,我正在使用用户输入未提供的 TempVars 存储的 var。本例中可以使用 DAO。也许在一些更复杂的情况下它无法工作,但这里没关系。

标签: vba ms-access dao pass-through


【解决方案1】:

始终设置连接字符串在设置 SQL 之前

当您设置 SQL 时,DAO 不知道这稍后会成为直通查询,因此它会尝试将其解析为 Access SQL,但显然失败了,因为它不是有效的 Access SQL。

只需更改顺序:

With qdf
    .Connect = getDBConnectionString
    .ReturnsRecords = True
    .SQL = strQuery
End With

请注意,您应该是using parameters,并且通常在处理外部数据源时使用 ADO 而不是 DAO。 DAO 非常适合 Access,但提供的外部数据源功能较少。例如,ADO 不会在实际需要之前尝试解析 SQL 字符串。

【讨论】:

    【解决方案2】:

    放弃对 DAO 和 QueryDefs 的需求,并使用带有命令参数化的 ADO,然后可以将其绑定到记录集:

    ' SET REFERENCE TO Microsoft ActiveX Data Object #.# Library
    Public Function getAssortmentTypes(Optional personId As Variant) As ADODB.Recordset
        Dim conn As ADODB.Connection
        Dim rst As ADODB.Recordset
        Dim cmd As ADODB.Command
    
        Set conn As New ADODB.Connection
        conn.Open getDBConnectionString
    
        ' PREPARED STATEMENT WITH QMARKS ?
        If IsMissing(personId) Then
            strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
        Else
            strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(?)"
        End If
    
        Set cmd = New ADODB.Command
        With cmd
            .ActiveConnection = conn
            .CommandText = strQuery
            .CommandType = adCmdText
    
            '  BIND PARAMETER
            .Parameters.Append .CreateParameter("user_param", adInteger, adParamInput, , personId)
    
            '  EXECUTE QUERY AND BIND INTO RECORDSET
            Set rst = .Execute
        End With
    
        Set cmd = Nothing
        Set getAssortmentTypes = rst
    End Function
    

    【讨论】:

      【解决方案3】:

      我不建议引入 ADO。

      问题看起来是您的第一个 SQL 查询将(并且确实)作为链接表工作,因此可以工作,因为它不是传递查询。

      第二个 sql 失败,因为它仍然尝试使用“访问”sql,而不是 postgresSQL 语法。

      我建议您创建一个 PT 查询(使用 Access UI)。在设计器中,确保选择 pass-though:

      因此,就像链接表一样 - 将连接字符串放入该 PT 查询中。

      不要在代码中放置或尝试放置连接字符串。因此,您的重新链接例程也可以包括重新链接 PT 查询。

      您现在可以使用此代码:

      Public Function getAssortmentTypes(Optional personId As Variant) As DAO.Recordset 'personId is integer
         
         Dim rst       As DAO.Recordset
         Dim strQuery  As String
         
         If IsMissing(personId) Then
             strQuery = "SELECT assortment_type.type_id, assortment_type.type_name AS qryTest FROM assortment_type"
         Else
             strQuery = "SELECT * FROM get_non_deleted_assortment_types_by_user(" & personId & ")"
         End If
         
         With CurrentDb.QueryDefs("qryPT")
            .SQL = strQuery
            Set rst = .OpenRecordset
         End With
         
         Debug.Print rst!qryTest
         Set getAssortmentTypes = rst
      
      End Function
      

      因此,创建一个名为(对于本例)qryPT 的 PT 查询

      【讨论】:

      • 这是一个非常“MS Access”特定的方法;)我不喜欢在我的程序中创建不必要的对象而且我很固执,所以我决定采用“仅代码”解决方案。请参阅下面 Eric 的回答,它解决了我的问题。
      • 我没有在代码中创建对象 - 所以不在代码中创建对象会建议我发布的解决方案,对吗?如您所见,我什至不必在代码中触摸、编写或设置连接。 Eric 的优秀解决方案在代码中创建了一个 querydef 对象(你说你不喜欢它),而且你还必须在代码中建立一个连接——再次在代码中创建更多的东西。奇怪的是,您建议您不想在代码中创建对象,然后转身采用一种解决方案,在该解决方案中,您既可以创建查询对象,又可以在代码中设置连接,而我的解决方案两者都没有?
      【解决方案4】:

      尝试省略“DAO”。 Recordset 和维度语句中的前缀。更高版本的 Access 了解您想要什么。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-07-30
        • 1970-01-01
        • 2019-04-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多