在 VBA 中执行存储过程的两种方法,哪一种更好?
Posted
技术标签:
【中文标题】在 VBA 中执行存储过程的两种方法,哪一种更好?【英文标题】:Two ways to execute a Stored procedure in VBA, Which one is better? 【发布时间】:2014-06-20 13:40:13 【问题描述】:背景:工作于前端 Ms-Access 2010 和后端 SQL server 2008 Managment Studio
为了执行存储过程,我一直在使用一个相当长的过程,如下所示:在 VBA 中
Set Conn = New ADODB.connection
Conn.ConnectionString = "Provider=SQLOLEDB.1;Integrated Security=SSPI;....."
Conn.Open
Set cmd = New ADODB.Command
cmd.ActiveConnection = Conn
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "upGetTestIdForAnalyte"
cmd.Parameters.Append cmd.CreateParameter("@WOID", adVarChar, adParamInput, 60, MySampleName)
cmd.Parameters.Append cmd.CreateParameter("@Analyte", adVarChar, adParamInput, 60, MyAnalyte)
cmd.Parameters.Append cmd.CreateParameter("@SampleID", adVarChar, adParamInput, 60, MyConcentration
cmd.Execute
Conn.Close
有人告诉我有一种更好的方法来执行存储过程,正确的方法是这样的:在 VBA 中
strsql = "Exec upGetTestIdForAnalyte(WOID, Analyte, SampleID)"
test = ExecuteNonQuery(strsql)
但是我在这个过程中遇到了很多错误,我查看了 ExecuteNonQuery,它说它只适用于 VB (http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.executenonquery.aspx)。此过程的优点之一是其简洁性以及它自动连接到当前数据库的事实。我将不得不更改当前 sp 进程中的连接字符串,因为它被设置为链接到测试数据库而不是实际数据库。另一个优点是它在执行时返回一个值。我希望我当前的流程能够做到这一点,因为我想以某种方式验证它是否运行,而我需要制作的其他存储过程需要返回受影响的记录数。
我的问题是:第二种方法是合法的过程吗?那是正确的语法吗?每个过程完成的内容也有什么区别吗?还有一种方法可以让第一个进程在执行时返回一个值吗?感谢您的宝贵时间。
更新:这是我目前正在处理的存储过程。我的 sp 查看 testID 是否存在,如果变量 ThisTestId >0,我将在调用 sp 后继续执行程序,否则我将引发错误 testid not found
CREATE PROCEDURE upGetTestIDForAnalyte @WOID nvarchar(60), @SampleID nvarchar(60),@Analyte nvarchar(60), @Test var(20) output
AS
SELECT TestID = t1.TestID
FROM tblWOSampleTest t1
JOIN tblTest t2
ON t1.TestID=t2.TestID;
WHERE @WOID = t1.WOID AND @SampleID = t1.SampleID AND @Analyte = t2.Analyte
GO
【问题讨论】:
ExecuteNonQuery
用于 .Net 的数据类,而不是用于 ADO。 ADO 中不使用 Parameter 对象的类似方法没有提供针对 SQL 注入的内置保护,这意味着您必须自己清理任何用户输入 - 这是它们的主要优势。如果您查看 ADODB.Command 调用的模式,您可以将连接/声明封装在单个函数中并调用它。
哦,好吧,所以基本上如果我使用 ADO 对象,第二种方法将不起作用
【参考方案1】:
这里发布的示例代码太多了。
最初的问题是发帖者看到了一些只需要一两行代码的例子。
事实上,这段代码可以工作并将参数传递给存储过程。
With CurrentDb.QueryDefs("MyPass")
.SQL = "exec MySproc" & ProducutID
.Execute
End If
上面只有两行代码。
这种方法的优点?
请注意我们如何不必弄乱(或传递)连接字符串。
注意我们不必在代码中声明任何连接对象。
请注意我们如何不必在代码中的任何位置存储或拥有用户名或密码。
注意我们也不必在代码中创建查询定义对象。
事实上,整个事情都不需要声明任何变量。
以上内容本可以用两行代码编写,但我决定发布大量的 4 行代码以提高可读性。
这种设置的优点很多,但主要原因是这种方法最简单且易于维护。
此处发布的其他解决方案仅用于迫使人们编写多行代码——这只会增加其雇主的开发成本。
【讨论】:
当然我的意思是输入 querydefs(如此编辑)。该参数是 VBA 长变量,因此不可能进行字符串和 SQL 注入)。在大多数情况下,该值来自强类型 VBA 变量,例如日期或数字。大多数表格都会拒绝多余的“;”当进入这样的控制。当然,一个例外是基于字符串的文本框。所以注入是一个问题,但是对于字符串,我有一个全局函数可以删除所有杂散符号,例如 () 和“;”。我的 2 行解决方案可能是唯一一个输入为空气代码的解决方案,这解释了错字。所以我更正为 querydefs("myPass") 那么“MyPass”是否代表传递给 ProductID 的值?如果是这样,将值传递给多个参数的语法是什么?还有没有办法知道有多少行受到影响? MyPass 是保存的 passthrough 查询的名称。如果您需要其他参数,则只需添加“,”和另一个值。语法与您在 sql 管理器中键入的语法相同。至于返回的行影响?我不知道 Store 程序每个返回行都会受到影响吗?但是,如果您的 sproc 的最后一行选择 @@RowCount,那么是的,受影响的记录将被返回。代替执行,您将因此使用: .OpenRecordset()("RecordsAffected") 假设存储过程的最后一行是 select @@rowcount as RecordsAffected【参考方案2】:我将继续使用第一个进程并添加如下输出参数:
ccmd.parameters.Append ccmd.CreateParameter("OutParam", adInteger, adParamOuput, , NULL) ' output parameter
您需要在存储过程中添加此参数,如下所示:
@OutParam int OUTPUT
EDIT 添加了 OUT 参数,在 VBA 代码中改为整数。看看@@ROWCOUNT 是如何工作的
CREATE PROCEDURE upGetTestIDForAnalyte @WOID nvarchar(60), @SampleID nvarchar(60),@Analyte nvarchar(60), @RecordsAfected int OUT
AS
SELECT TestID = t1.TestID
FROM tblWOSampleTest t1
JOIN tblTest t2
ON t1.TestID=t2.TestID;
WHERE @WOID = t1.WOID AND @SampleID = t1.SampleID AND @Analyte = t2.Analyte
set @recordsAfected = @@ROWCOUNT
GO
【讨论】:
好的,所以一旦我将此输出参数添加到 sp 和进程中,可以将 Outparam 用作变量吗?如果我将 OutParam 设置为等于我的 sp 中的某个值,那么在执行 sp 以检索值之后,我是否可以在我的过程中设置 outparam 等于某个值? @VictoriaJay 完全正确。如果您需要此步骤的帮助,请分享您的 sp 我在我的 VBA 代码中使用了这个,所以 ThisTestID 可以存储值,这是正确的语法吗?cmd.Parameters.Append cmd.CreateParameter("@recordsAffected", adInteger, adParamOutput, 20, ThisTestID)
@VictoriaJay 大小是可选的,最好这样: cmd.Parameters.Append cmd.CreateParameter("@recordsAffected", adInteger, adParamOutput, , ThisTestID)【参考方案3】:
来自Microsoft help site:
Function ExecuteSPT (sqltext As String, connectstring As String)
' Purpose: Run a temporary pass-through query.
' Accepts: sqltext: SQL string to run.
' connectstring: Connection string, which must be at least
' "ODBC;".
' Returns: nothing.
Dim mydb As Database, myq As QueryDef
Set mydb = DBEngine.Workspaces(0).Databases(0)
' Create a temporary QueryDef object that is not saved.
Set myq = mydb.CreateQueryDef("")
' Set the ReturnsRecords property to False in order to use the
' Execute method.
myq.returnsrecords = False
myq.connect = connectstring
myq.sql = sqltext
myq.Execute
myq.Close
End Function
它可以很容易地更改为以 DAO.Recordset 的形式返回。 DAO 仍然是 MS Access 中的“本机”数据访问。
【讨论】:
当你说代码可以被修改以返回一个 DAO.Recordset,你的意思是什么? DAO = 数据访问对象,这是 Access 的标准模型。也可以使用 ADO 等其他方法,但如果在 Form 中使用Me.Recordset
,则会得到 DAO.Recordset。以上是关于在 VBA 中执行存储过程的两种方法,哪一种更好?的主要内容,如果未能解决你的问题,请参考以下文章