如何从VBA ADODB中的参数化SQL Server查询诊断语法错误

时间:2019-01-05 18:33:19

标签: sql-server excel vba excel-vba adodb

我正在使用VBA adodb在基于Excel行内容的SQL Server中编写INSERT语句。

我正在使用参数化查询,因此我的VBA代码具有以下形式:

sqlStatement = "INSERT INTO dbo.mytable (" & Join(insertElement(0), ", ") & ") VALUES (" & Join(insertElement(2), ", ") & ")"
Set cm = New ADODB.Command

With cm
      Debug.Print (sqlStatement)
            .ActiveConnection = conn
            .CommandText = sqlStatement
            .CommandType = adCmdText
             For Each e In insertElement(1)
                Set Pm = .CreateParameter(, adVarChar, 3, 1024, e)
                .Parameters.Append Pm
             Next e
             Set rs = .Execute
        End With

其中insertElement(0)是字段名称的数组,...(1)是值的数组,而...(2)是占位符?的数组,以支持参数化

运行此代码时,出现错误

  

[Microsoft] [用于SQL Server的ODBC驱动程序13] [SQL Server]“输出”附近的语法不正确

但是,当我询问sqlStatement文本时,语句中的任何地方都没有“输出”。文本格式为:

INSERT INTO dbo.mytable ([my_field_1],[my_field_2],[somefieldshaveweirdcharslike+#], ...) VALUES (?, ?, ?, ...) 

因此,如果我没有直接提供“输出”命令,并且由于正在服务器端处理该语句而无法直接看到该语句,那么如何诊断语法?

2 个答案:

答案 0 :(得分:0)

您应该能够通过迭代数组并以您认为合适的方式串联值来简单地构建SQL语句,而不是尝试将一堆参数插入一堆问号。像这样(我假设insertElement实际上是一个二维数组而不是一个集合;如果它确实是一个集合,那么您将需要与For Each做类似的事情):

sqlStatement = "INSERT INTO dbo.mytable (" & Join(insertElement(0), ", ") & ") VALUES ("
Set cm = New ADODB.Command

With cm
    .ActiveConnection = conn
    .CommandType = adCmdText
    Dim aCount As Integer
    aCount = Ubound(insertElement(1))
    For i = 0 To aCount - 1
        sqlStatement = sqlStatement & "'" & insertElement(1,i) & "'" & _
        Iif(i <> aCount - 1, ",", "")
    Next
    .CommandText = sqlStatement
    Set rs = .Execute
End With

所有这些,如果您想更深入地了解错误的发生位置,可以尝试迭代Connection对象的Errors collection。由于尝试使用连接会产生多个错误,因此仅使用Err对象就很难进行故障排除。

最后,最好是在服务器端的存储过程中设置INSERT语句,并以这种方式将值作为参数传递,除非您没有理由担心注入攻击。

答案 1 :(得分:0)

我在SQL Server中没有跟踪权限,因此我在探索服务器端日志记录方面的选择有限。

我最终解决了这种蛮力方式,一次添加一个字段直到收到错误。

就我而言,根本问题与将adodb adNumeric参数类型用于十进制值有关。我将参数类型切换为adDecimal,然后在各个参数对象上设置了NumericScalePrecision的值。 vba代码结构如下所示

            For Each p In paramArr
                If p(1) = adVarChar Then
                    Set Pm = .CreateParameter(, p(1), 1, Len(p(2)), p(2))
                Else
                    Set Pm = .CreateParameter(, p(1), 1, , p(2))
                End If
                If p(1) = adDecimal Then
                    Pm.NumericScale = 3
                    Pm.Precision = 13
                End If
                .Parameters.Append Pm
            Next p