VBA,ADO.Connection和查询参数

Ale*_*xey 22 vba ado

我有excel VBA脚本:

Set c?nn = CreateObject("ADODB.Connection")
conn.Open "report"
Set rs = conn.Execute("select * from table" ) 
Run Code Online (Sandbox Code Playgroud)

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

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

Dic*_*ika 42

您需要使用可以添加参数的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
Run Code Online (Sandbox Code Playgroud)

CommandText中的问号是参数的占位符.我相信,但我不是肯定的,你追加参数的顺序必须与问号的顺序相匹配(当你有多个时).不要被愚弄,参数被命名为"parentid",因为我不认为ADO关心除了识别之外的名称.

  • @ LuigiMackenzieC.Brito违背了使用参数化查询的目的.使用包含userinput数据的变量的串联来构建SQL查询字符串是一种不好的做法.(因为如果myparam ="parentid; SELECT*from userpasswords",或者如果连接用户具有写权限:myparam ="parentid; insert into userpasswords ..."或者甚至是to trolly,myparam ="parentid;从表") (11认同)
  • @ LuigiMackenzieC.Brito - 但是你没有在评论中提到这一点,因此有人可能会在不知道SQL注入的情况下使用你的方法.即使您以某种方式过滤掉危险代码(我非常怀疑),对查询执行字符串连接也不是一个好习惯. (7认同)
  • 我相信你对参数的顺序是正确的.当我来自C#.Net然后编码VBScript时,这让我兴奋不已. (2认同)

Dav*_*edy 8

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

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
Run Code Online (Sandbox Code Playgroud)

需要注意的几件事:

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

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

  2. 如果该cmd.ActiveConnection属性是在设置时cmd.CommandText设置的,并且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
    
    Run Code Online (Sandbox Code Playgroud)

    我相信这就是文档中的含义,该文档有些不准确:

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

    解决方法是设置参数cmd.CommandTextcmd.ActiveConnection添加参数后。

  • 每当我尝试这样做时,我总是会收到运行时错误“必须声明标量变量“@name”” (3认同)
  • 为此,我认为您还需要 [`cmd.NamedParameters = True`](https://learn.microsoft.com/en-us/sql/ado/reference/ado-api/namedparameters-property-ado )并且连接必须使用 [支持命名参数的 ADO 提供程序](https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/configuring-parameters-and-parameter-data-types#使用参数占位符)(例如 SqlClient,而不是 ODBC/OLEDB)。 (2认同)