我真的不明白这里发生了什么。我使用Excel VBA连接到SQL Server Express数据库并返回ADO Recordset。我最初工作了,但是我的代码有点乱,所以我创建了一个新模块并复制了代码,整理过程。
现在,当我尝试运行Sub只是为了返回记录计数时,我得到错误"当对象关闭时,不允许该操作。"代码在MsgBox行上断开。
以下是简化代码:
Dim Server As String
Server = "ServerName"
Dim Database As String
Database = "DatabaseName"
Dim UserID As String
UserID = "UserID"
Dim Pwd As String
Pwd = "Password"
Dim StoredProcedure As String
StoredProcedure = "StoredProcedureName"
Dim conn As New ADODB.Connection
conn.ConnectionString = "Driver={SQL Server};Server=" & Server & "; Database=" & Database & "; UID = " & UserID & "; PWD=" & Pwd & ""
conn.Open
Dim Cmd As New ADODB.Command
Cmd.ActiveConnection = conn
Cmd.CommandText = StoredProcedure
Cmd.CommandType = adCmdStoredProc
Dim params() As String
ReDim Preserve params(4, 2)
params(0, 0) = "Param1"
params(1, 0) = CStr(adInteger)
params(2, 0) = CStr(adParamInput)
params(4, 0) = CStr(6)
params(0, 1) = "Param2"
params(1, 1) = CStr(adInteger)
params(2, 1) = CStr(adParamInput)
params(4, 1) = CStr(6)
params(0, 2) = "Param3"
params(1, 2) = CStr(adInteger)
params(2, 2) = CStr(adParamInput)
params(4, 2) = CStr(15)
Dim sParam4 as String
If Not sParam4 = "" Then
ReDim Preserve params(4, UBound(params, 2) + 1)
params(0, UBound(params, 2)) = "Param4"
params(1, UBound(params, 2)) = CStr(adChar)
params(2, UBound(params, 2)) = CStr(adParamInput)
params(3, UBound(params, 2)) = "1"
params(4, UBound(params, 2)) = sParam4
End If
Dim sParam5 as String
If Not sParam5 = "" Then
ReDim Preserve params(4, UBound(params, 2) + 1)
params(0, UBound(params, 2)) = "Param5"
params(1, UBound(params, 2)) = CStr(adChar)
params(2, UBound(params, 2)) = CStr(adParamInput)
params(3, UBound(params, 2)) = Len(sParam5)
params(4, UBound(params, 2)) = sParam5
End If
Dim Prm As ADODB.Parameter
Set Prm = New ADODB.Parameter
Dim i As Integer
For i = 0 To UBound(params, 2)
If params(1, i) = CStr(adChar) Then
Set Prm = Cmd.CreateParameter(params(0, i), CInt(params(1, i)), CInt(params(2, i)), CInt(params(3, i)))
Cmd.Parameters.Append Prm
Cmd.Parameters(params(0, i)).Value = params(4, i)
Else
Set Prm = Cmd.CreateParameter(params(0, i), CInt(params(1, i)), CInt(params(2, i)))
Cmd.Parameters.Append Prm
Cmd.Parameters(params(0, i)).Value = CInt(params(4, i))
End If
Next i
Dim rs As New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open Cmd, , adOpenStatic, adLockOptimistic
MsgBox ("Success! " & rs.RecordCount & " Records Returned!")
当我在Locals窗口中查看rs变量时,所有属性都列为显示相同的错误消息。对我来说,似乎记录集正确打开,但随后立即关闭。
奇怪的是,原来(凌乱)的子现在也不起作用,抛出同样的错误。我不认为我在那里改变了任何东西,只是把它复制到新的子。
我已经注释了旧子所在的整个模块,以防万一存在任何类型的冲突变量情况。这并没有任何区别。
我只是看不出它有什么问题!我已经做了大量的研究和阅读,而且对于我未经训练但热情的眼睛,这一切似乎都很好。
非常感谢任何帮助。
编辑:这是存储过程:
USE [MyDatabase]
GO
/****** Object: StoredProcedure [dbo].[MyProcedure] Script Date: 14/09/2015 11:39:00 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE [dbo].[MyProcedure]
-- Add the parameters for the stored procedure here
@Param1 int, @Param2 int, @Param3 int, @Param4 char(1), @Param5 varchar(20) = NULL, @Param6 varchar(20) = NULL, @Param7 varchar(20) = NULL
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET ANSI_WARNINGS OFF;
-- Insert statements for procedure here
DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX)
SET @cols = '';
SELECT @cols = @cols + ',' + QUOTENAME([Field3]) FROM View1
WHERE Field2 = @Param2 AND Field1 = @Param1
SET @cols = STUFF(@cols,1,1,'');
IF @Param5 IS NOT NULL
SET @query = 'SELECT Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11, Field12, Field13, Field14,' + @cols + ' FROM
(
SELECT
Table1.Field1,
Table1.Field2,
Table1.Field3,
Table1.Field4,
Table1.Field5,
Table1.Field6,
Table1.Field7,
Table1.Field8,
Table1.Field9,
Table1.Field10,
Table1.Field11,
Table1.Field12,
Table1.Field13,
Table1.Field14,
Table2.Field2,
Table3.Field1
FROM Table3
LEFT OUTER JOIN Table2 ON Table3.Field1 = Table2.Field1
AND Table3.Field2 = Table2.Field2
LEFT OUTER JOIN Table4 ON Table3.Field4 = Table4.Field1
AND Table3.Field2 = Table4.Field3
RIGHT OUTER JOIN Table1 ON Table2.Field1 = Table1.Field1
AND Table2.Field2 = Table1.Field15
WHERE Table1.Field2 = ' + CAST(@Param3 AS char(1)) + '
AND Table1.Field12 = ''' + @Param5 + '''
AND Table1.Field15 = ' + CAST(@Param1 AS char(2)) + '
)
AS UP
PIVOT(MAX(Field2) FOR UP.ID IN (' + @cols + ')) AS PVT '
ELSE IF @Param6 IS NOT NULL
SET @query = 'SELECT Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11, Field12, Field13, Field14,' + @cols + ' FROM
(
SELECT
Table1.Field1,
Table1.Field2,
Table1.Field3,
Table1.Field4,
Table1.Field5,
Table1.Field6,
Table1.Field7,
Table1.Field8,
Table1.Field9,
Table1.Field10,
Table1.Field11,
Table1.Field12,
Table1.Field13,
Table1.Field14,
Table2.Field2,
Table3.Field1
FROM Table3
LEFT OUTER JOIN Table2 ON Table3.Field1 = Table2.Field1
AND Table3.Field2 = Table2.Field2
LEFT OUTER JOIN Table4 ON Table3.Field4 = Table4.Field1
AND Table3.Field2 = Table4.Field3
RIGHT OUTER JOIN Table1 ON Table2.Field1 = Table1.Field1
AND Table2.Field2 = Table1.Field15
WHERE Table1.Field2 = ' + CAST(@Param3 AS char(1)) + '
AND Table1.Field13 = ''' + @Param6 + '''
AND Table1.Field15 = ' + CAST(@Param1 AS char(2)) + '
)
AS UP
PIVOT(MAX(Field2) FOR UP.ID IN (' + @cols + ')) AS PVT '
ELSE IF @Param7 IS NOT NULL
SET @query = 'SELECT Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11, Field12, Field13, Field14,' + @cols + ' FROM
(
SELECT
Table1.Field1,
Table1.Field2,
Table1.Field3,
Table1.Field4,
Table1.Field5,
Table1.Field6,
Table1.Field7,
Table1.Field8,
Table1.Field9,
Table1.Field10,
Table1.Field11,
Table1.Field12,
Table1.Field13,
Table1.Field14,
Table2.Field2,
Table3.Field1
FROM Table3
LEFT OUTER JOIN Table2 ON Table3.Field1 = Table2.Field1
AND Table3.Field2 = Table2.Field2
LEFT OUTER JOIN Table4 ON Table3.Field4 = Table4.Field1
AND Table3.Field2 = Table4.Field3
RIGHT OUTER JOIN Table1 ON Table2.Field1 = Table1.Field1
AND Table2.Field2 = Table1.Field15
WHERE Table1.Field2 = ' + CAST(@Param3 AS char(1)) + '
AND Table1.Field14 = ''' + @Param7 + '''
AND Table1.Field15 = ' + CAST(@Param1 AS char(2)) + '
) AS UP
PIVOT(MAX(Field2) FOR UP.ID IN (' + @cols + ')) AS PVT '
ELSE
SET @query = 'SELECT Field1, Field2, Field3, Field4, Field5, Field6, Field7, Field8, Field9, Field10, Field11, Field12, Field13, Field14,' + @cols + ' FROM
(
SELECT
Table1.Field1,
Table1.Field2,
Table1.Field3,
Table1.Field4,
Table1.Field5,
Table1.Field6,
Table1.Field7,
Table1.Field8,
Table1.Field9,
Table1.Field10,
Table1.Field11,
Table1.Field12,
Table1.Field13,
Table1.Field14,
Table2.Field2,
Table3.Field1
FROM Table3
LEFT OUTER JOIN Table2 ON Table3.Field1 = Table2.Field1
AND Table3.Field2 = Table2.Field2
LEFT OUTER JOIN Table4 ON Table3.Field4 = Table4.Field1
AND Table3.Field2 = Table4.Field3
RIGHT OUTER JOIN Table1 ON Table2.Field1 = Table1.Field1
AND Table2.Field2 = Table1.Field15
WHERE Table1.Field2 = ' + CAST(@Param3 AS char(1)) + '
AND Table1.Field3 = ''' + @Param4 + '''
AND Table1.Field15 = ' + CAST(@Param1 AS char(2)) + '
) AS UP
PIVOT(MAX(Field2) FOR UP.ID IN (' + @cols + ')) AS PVT '
EXECUTE (@query)
Set NOCOUNT OFF;
END
我知道参数背后的逻辑并非......逻辑......但它现在的工作方式是我需要的。我正在努力改变这一点。
我还应该说我在第一次发布问题时省略了处理SP参数的VBA代码部分。我现在刚刚离开工作,但是一旦我回到家,我就会把它添加进来。我不认为问题就在那里。我基本上通过参数的字符串数组并将它们附加到Cmd
对象。
编辑:我现在已经添加了用于创建参数并将它们传递给Cmd
对象的代码。同样,它有点令人费解,但它是当时我脑子里出来的东西,它确实有效。一旦记录集被填充,我就会重新审视逻辑。
答案 0 :(得分:1)
评论有点长,所以我会把它作为一个可能的答案。请尝试:
ValueChanged
答案 1 :(得分:1)
请尝试以下代码:
Public Sub AdoTestConnection()
Dim conServer As ADODB.Connection
Dim rstResult As ADODB.Recordset
Dim strDatabase As String
Dim strServer As String
Dim strSQL As String
strServer = "YourServerName"
strDatabase = "YourDatabaseName"
Set conServer = New ADODB.Connection
conServer.ConnectionString = "PROVIDER=SQLOLEDB; " _
& "DATA SOURCE=" & strServer & "; " _
& "INITIAL CATALOG=" & strDatabase & "; " _
& "User ID=" & strLogin & ";" _
& "Password=" & strPassword
On Error GoTo SQL_ConnectionError
conServer.Open
On Error GoTo 0
Set rstResult = New ADODB.Recordset
strSQL = "set nocount on; "
strSQL = strSQL & "select 1 "
rstResult.ActiveConnection = conServer
On Error GoTo SQL_StatementError
rstResult.Open strSQL
On Error GoTo 0
If Not rstResult.EOF And Not rstResult.BOF Then
MsgBox "Connection worked. Server returned " & rstResult.Fields(0).Value
Else
MsgBox "Connection worked. The server did not return any value."
End If
Exit Sub
SQL_ConnectionError:
MsgBox "Problems connecting to the server." & Chr(10) & "Aborting..."
Exit Sub
SQL_StatementError:
MsgBox "Connection established. Yet, there is a problem with the SQL syntax." & Chr(10) & "Aborting..."
Exit Sub
End Sub
如果以上代码有效,那么您可以使用如下过程更改SQL命令:
strSQL = "set nocount on; "
strSQL = strSQL & "exec StoredProcedureName @Parm1 = " & intValue1 & ", "
strSQL = strSQL & " @Parm2 = " & intValue2 & ", "
strSQL = strSQL & " @Parm3 = " & intValue3 & ", "
strSQL = strSQL & " @Parm4 = N'" & strValue1 & "', "
strSQL = strSQL & " @Parm5 = N'" & strValue2 & "', "
strSQL = strSQL & " @Parm6 = N'" & strValue3 & "', "
strSQL = strSQL & " @Parm7 = N'" & strValue4 & "' "
我非常支持这种方法,因为它更容易调试。如果您遇到SQL语法问题,您可以简单地请求strSQL
的内容:
Debug.Print strSQL
然后,您可以将其结果复制到SQL Server Management Studio(SSMS)并验证结果。 您甚至可以得出结论,您不想使用存储过程并将SP的整个内容复制到您的VBA代码中。
答案 2 :(得分:1)
我遇到了类似的问题,发现有两件事通过ODBC
杀死了查询结果在最终输出之前处理的中间值的计数。修复了查询开头的“set nocount on”
聚合的空值 - 直接在SQL Server上运行查询显示消息“警告:聚合或其他SET操作消除了空值”。通过追逐它们中的每一个并用空字符串替换,零或任何低值是有意义的,并且不会影响查询结果。
我认为在这两种情况下都会在记录之前输出警告消息,因此存储记录集被“填充”了该警告,但没有数据。在SQL Server上,这些警告静默地位于消息日志中,但不会影响查询结果,因此诱惑是让它们成为更高优先级的工作。