【问题标题】:How to pass table valued parameters into stored procedure from excel vba如何将表值参数从excel vba传递到存储过程
【发布时间】:2018-08-25 06:24:43
【问题描述】:

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

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 = 处出错

谢谢

【问题讨论】:

标签: sql-server excel vba


【解决方案1】:

据我所知,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

【讨论】:

    猜你喜欢
    • 2012-09-24
    • 2015-11-13
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多