VBA、ADO.Connection 和查询参数

Posted

技术标签:

【中文标题】VBA、ADO.Connection 和查询参数【英文标题】:VBA, ADO.Connection and query parameters 【发布时间】:2012-05-08 07:04:44 【问题描述】:

我有 excel VBA 脚本:

Set cоnn = CreateObject("ADODB.Connection")
conn.Open "report"
Set rs = conn.Execute("select * from table" ) 

脚本工作正常,但我想为其添加参数。例如“ where (parentid = myparam)”,其中 myparam 设置在查询字符串之外。我该怎么做?

当然我可以修改查询字符串,但我认为这不是很明智。

【问题讨论】:

【参考方案1】:

您需要使用可以添加参数的 ADODB.Command 对象。基本上是这样的

Sub adotest()

    Dim Cn As ADODB.Connection
    Dim Cm As ADODB.Command
    Dim Pm As ADODB.Parameter
    Dim Rs as ADODB.Recordset

    Set Cn = New ADODB.Connection
    Cn.Open "mystring"
    Set Cm = New ADODB.Command
    With Cm
        .ActiveConnection = Cn
        .CommandText = "SELECT * FROM table WHERE parentid=?;"
        .CommandType = adCmdText

        Set Pm = .CreateParameter("parentid", adNumeric, adParamInput)
        Pm.Value = 1

        .Parameters.Append Pm

        Set Rs = .Execute
    End With

End Sub

CommandText 中的问号是参数的占位符。我相信,但我并不肯定,您附加参数的顺序必须与问号的顺序相匹配(当您有多个问号时)。不要因为参数被命名为“parentid”而被愚弄,因为我认为 ADO 不关心名称,而不是用于标识。

【讨论】:

我相信你在参数顺序上是正确的。当我来自 C# .Net 然后编写 VBScript 时,这让我很困惑。 或者你可以... .CommandText = "SELECT * FROM table WHERE parentid=" & myparam & ";" @LuigiMackenzieC.Brito 违背了使用参数化查询的目的。使用包含用户输入数据的变量连接来构建 SQL 查询字符串是一种不好的做法。 (因为如果 myparam = "parentid;SELECT * from userpasswords",或者如果连接用户具有写入权限:myparam="parentid;insert into userpasswords..." 或者只是推车,myparam="parentid;delete from表") @cowbert SQL 注入是的。在我的实践中,我使用 php 将它们过滤掉。 @LuigiMackenzieC.Brito -- 但是您没有在评论中提及这一点,因此存在有人可以在不知道 SQL 注入的情况下使用您的方法的危险。即使您以某种方式过滤掉了危险代码(我非常怀疑),对查询进行字符串连接也不是一个好习惯。【参考方案2】:

从函数返回命令的替代示例:

Function BuildCommand(conn As ADODB.Connection) As ADODB.Command
    Dim cmd As ADODB.Command
    Set cmd = New ADODB.Command
    cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    cmd.Parameters.Append cmd.CreateParameter("@name", adVarChar, adParamInput, 255, "Dave")
    cmd.CommandText = "SELECT * FROM users WHERE name = @name;"
    Set BuildCommand = cmd
End Function

需要注意的几点:

    当使用adVarChar 数据类型时,cmd.CreateParameter 的大小参数(例如 255)是必需的。不提供它会导致运行时错误 3708:应用程序定义或对象定义错误,如 documentation 中所示:

    如果在 Type 参数中指定可变长度数据类型,则必须在将其附加到 Parameters 集合之前传递 Size 参数或设置 Parameter 对象的 Size 属性;否则,会发生错误。

    如果在设置cmd.CommandText 时设置了cmd.ActiveConnection 属性,并且cmd.CommandText 包含命名参数,则将相应地填充cmd.Parameters。之后调用cmd.Parameters.Append 可能会导致重复。例如:

    cmd.ActiveConnection = conn
    cmd.CommandType = adCmdText
    Debug.Print cmd.Parameters.Count ' 0
    
    cmd.CommandText = "SELECT * FROM users WHERE name = @name;"
    Debug.Print cmd.Parameters.Count ' 1
    
    cmd.Parameters.Append cmd.CreateParameter("@name", adVarChar, adParamInput, 255, "Dave")
    Debug.Print cmd.Parameters.Count ' 2
    

    我相信这就是documentation 中的意思,这有点不准确:

    如果 Command 对象的 Prepared 属性设置为 True,并且在您设置 CommandText 属性时,Command 对象绑定到打开的连接,ADO 会准备查询(即,查询的编译形式存储在提供者)当您调用 Execute 或 Open 方法时。

    作为一种解决方法,在添加参数后设置cmd.CommandTextcmd.ActiveConnection

【讨论】:

每当我尝试这样做时,总是会收到运行时错误Must Declare the scalar variable "@name" 当需要设置多个参数时,这会使代码看起来更清晰。谢谢。 为此,我认为您还需要 cmd.NamedParameters = True 并且连接必须使用 ADO provider which supports named parameters(例如 SqlClient,而不是 ODBC/OLEDB)。 @Kevinoid 看起来只使用“?”作为 odbc/oledb 的参数占位符。对吗? @Sacru2red 我相信这是正确的。据我所知,如果您使用 ADO 的 ODBC 或 OleDB 提供程序,? 是唯一受支持的参数占位符。

以上是关于VBA、ADO.Connection 和查询参数的主要内容,如果未能解决你的问题,请参考以下文章

使用 vba 在访问查询中传递参数值

VBA根据查询运行带有参数的访问报告

VBA/SQL 参数化查询 - 字段列表中的未知列

VBA ADO 参数化 LIKE 查询

如何在 Microsoft Access 中通过 VBA 设置 INSERT SQL 查询的参数值?

为 VBA 记录集传递参数