如何从Excel VBA将表值参数传递到存储过程

时间:2018-08-25 06:24:44

标签: sql-server excel vba

请建议如何从excel vba将表值参数传递到sql服务器过程中。

Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim constring As String
conn.ConnectionString="My Connection String"
conn.Open
With cmd
.ActiveConnection = conn
.CommandType = adCmdText
.CommandText = "declare @tbl tbl insert into @tblvalues('fdsdf',15)"
.Execute
End With

我的表类型如下:

CREATE TYPE [tbl] AS TABLE 
(
myNO VARCHAR(10),
myVALUE FLOAT
)

我的程序如下:

create procedure mydata
(
@tbl tbl readonly
)
as 
select * into #tbl from @tbl
select * from #tbl
end

现在,我想通过输入此@tbl

来执行该过程。
With cmd
.ActiveConnection = conn
.CommandType = adCmdStoredProc
**param = .CreateParameter("@po", adUserDefined, adParamInput)
.Parameters("@po").Value = param**
.CommandText = "mydata"
Set rs = .Execute
End With

请建议我在.Parameters(“ @ po”)。Value =

时出错。

谢谢

1 个答案:

答案 0 :(得分:0)

据我所知,Excel VBA支持的adodb不支持表值参数。参见http://www.sommarskog.se/arrays-in-sql-2008.html#ADO

解决方法是创建一个包装的存储过程,该存储过程将转换某些其他数据结构,例如发送用逗号/分号分隔的列表或XML数据,并将其解析为表变量,或者发送要构建的命令批处理表变量。 http://www.sommarskog.se/arrays-in-sql-2008.html#Workarounds

给出的示例如下。 www.sommarskog.se没有使用XML的示例。

存储过程(请注意,在此示例中,您将调用“ get_product_names_wrapper”)

CREATE PROCEDURE get_product_names_wrapper @prodids nvarchar(MAX)
DECLARE @prodid_table integer_list_tbltype
INSERT @prodid_table(n)
    SELECT number FROM intlist_to_tbl(@prodids)
EXEC get_product_names @prodid_table

参数化命令批处理:

DECLARE @prodid_table integer_list_tbltype
INSERT @prodid_table(n)
    SELECT number FROM iter_intlist_to_tbl(?)
EXEC get_product_names @prodid_table

我还建议您可以发送动态命令批处理。这将意味着每次都会重新创建执行计划,但是如果发送字符串列表,它将消除定界符也包含在数据中的问题。它也可以更容易地扩展以解决发送多列数据的问题。请注意,您可以将其扩展为使用参数,而不是对数据进行硬编码,但是,在许多情况下,假设每次调用之间的行数不一致,它仍然是动态的。此外,这可能导致诸如查询计划缓存膨胀和SQL注入问题(在处理字符串时)之类的问题。请注意,如果您正确处理了将数据推送到SQL命令中的数据,则可以消除SQL注入。 (请参见下面的sql 2005链接中的数组)

此示例未演示字符串数据或多列。它只是遵循前面示例中的模式。

DECLARE @prodid_table integer_list_tbltype
INSERT @prodid_table(n)
VALUES (1), (22), (45)
EXEC get_product_names @prodid_table

另一种选择是创建并填充临时表,然后将其内容推送到存储过程。这样可以消除该批处理中查询计划缓存和SQL注入的问题,但在此之前需要额外的代码来创建和填充临时表。

DECLARE @prodid_table integer_list_tbltype
INSERT @prodid_table(n)
SELECT ProdID FROM #tmpProdIDList
EXEC get_product_names @prodid_table

请注意,本文是一本不错的书,它提供了所有选项以及其中一些限制:www.sommarskog.se/arrays-in-sql-2005.html

该文章提到SQL注入是某些选项的关注点,但没有说明如果正确处理它就不是问题。另外,在提到动态SQL时,它是指存储过程内部。要消除SQL注入,将需要解析传入的字符串值,将其拆分,并根据需要进行组合,以确保没有将额外的命令“注入”到数据中。我通常不关心存储过程中的SQL注入,因为我对存储过程的控制有限,并且希望它们尽可能高效地运行,因此我不编写它们来允许这样做。相反,我关心的是我编写的任何动态SQL。

因此,我不会这样写(CustomerList通常类似于“'Franks','Johns','Toms'“):

rs.Open "SELECT * FROM Customers WHERE CompanyName IN (" & CustomerList & ")", cn

相反,我会这样写(假设CustomerList具有数据/已定义。它是一个像{Franks,Johns,Toms}的数组)

For Index = 0 To UBound(CustomerList)
    Customers = Customers & "'" & Replace(CustomerList(Index), "'", "''") & "',"
Next
Customers = Left$(Customers, Len(Customers) - 1)
rs.Open "SELECT * FROM Customers WHERE CompanyName IN (" & Customers & ")", cn