2015-09-14 2 views
3

Я действительно не понимаю, что здесь произошло. Я использую Excel VBA для подключения к базе данных SQL Server Express и возвращаю ADO Recordset. Сначала я работал, но мой код был немного беспорядочным, поэтому я создал новый модуль и скопировал код, убрав его, когда я пошел.ADO Recordset не остается открытым в VBA - «Операция не допускается, когда объект закрыт»

Теперь, когда я пытаюсь запустить 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!") 

Когда я смотрю на переменную Rs в окне Locals, все свойства перечислены как показывает то же самое сообщение об ошибке. Для меня кажется, что набор записей открывается правильно, но затем сразу же закрывается.

Что странно в том, что оригинальная (грязная) подставка теперь также не работает, бросая ту же ошибку. Я не думаю, что я что-то там что-то изменил, просто скопировал его на новый.

Я прокомментировал весь модуль, в котором находился старый юзер, на всякий случай, когда происходит какая-то конфликтная переменная. Это не имело никакого значения.

Я просто не вижу, что с этим не так! Я много исследовал и читал, и к моему неподготовленному, но восторженному глазу, все кажется прекрасным.

Любая помощь будет высоко оценена.

EDIT: Вот хранимая процедура:

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 

Я знаю, что логика параметров не очень ... логично .. но это работает так, как мне нужно это сейчас. Это то, что я изменил, когда я работаю над этим.

Я также должен сказать, что я пропустил раздел моего кода VBA, который обрабатывает параметры для SP, когда я впервые разместил вопрос. Сейчас я просто ухожу, но я добавлю его, когда вернусь домой. Я не думаю, что проблема там. Я в основном работаю через строковый массив параметров и добавляю их к объекту Cmd.

EDIT: Я добавил код для создания параметров и передачи их объекту Cmd. Опять же, это немного запутанно, но это то, что вышло из моей головы в то время, и оно действительно работает. Я вернусь к логике после заполнения набора записей.

+0

Предлагаю вам научиться никогда не использовать 'Dim ... As New'. Используйте две строки: 'Dim rs As ADODB.Recordset', затем' Set rs = New ADODB.Recordset'. Затем попробуйте выполнить код по строкам. – Rory

+0

@Rory Ah man, я изначально отделил выражения 'Dim' и' Set', но, исследуя, что может быть неправильно, я изменил их все! Я поменяю их прямо назад. Просто из интереса, в чем причина того, чтобы делать это правильно? –

+1

Я бы также предложил, чтобы вы посмотрели свою хранимую процедуру. Если он не возвращает записи, вы получите закрытый набор записей. Это также может произойти, если сначала выполняется какая-то другая обработка, и вы не использовали 'SET NOCOUNT ON' – Rory

ответ

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 & "' " 

I сильно одобряйте этот подход по сравнению с вашим текущим, потому что его гораздо легче отлаживать.Если вы когда-нибудь столкнетесь с проблемой с помощью синтаксиса SQL можно просто запросить содержание strSQL так:

Debug.Print strSQL 

Затем вы можете скопировать результат того, что в SQL Server Management Studio (SSMS) и проверить результат есть , Вы даже можете прийти к выводу, что вы не хотите использовать хранимую процедуру и скопировать весь контент SP в код VBA.

+0

Сработало - я получаю «Соединение работает. Сервер вернулся 1». Однако причиной того, что я использую 'rs.open Cmd' для открытия набора записей, является то, что я динамически добавлял кучу объектов параметров в объект' Cmd'. –

+0

Я обновил ответ, чтобы включить ваши параметры. – Ralph

+0

Правильно .. Это работает! Теперь набор записей заполняется, как ожидалось. Спасибо! Не совсем уверен, что делать сейчас. Я рад отметить это как ответ, так как вы решили для меня немедленную проблему в проекте, над которым я работаю. Тем не менее, я также хотел бы знать, почему мой оригинальный код не работает. От чтения документации и просмотра множества примеров, не говоря уже о том, что у меня был тот же самый код, который работал ранее, мой исходный код должен сработать! Ясно, что я сделал что-то не так, но я просто хотел бы знать, что. Должен ли я просто задать новый вопрос: «Почему это не работает?» –

1

Это немного длинный комментарий, поэтому я поставлю его здесь как возможный ответ. Пожалуйста, попробуйте:

Dim rs As ADODB.Recordset 
Set rs = New ADODB.Recordset 
With rs 
    Set .ActiveConnection = conn 
    .LockType = adLockOptimistic 
    .CursorLocation = adUseServer 
    .CursorType = adOpenForwardOnly 
    .Open "SET NOCOUNT ON" 
End With 
rs.Open Cmd, , , , adCmdStoredProc 

MsgBox ("Success! " & rs.RecordCount & " Records Returned!") 
+0

Извините, не повезло, все равно получите то же сообщение об ошибке. Я просто работаю над сортировкой кода SP, чтобы я мог опубликовать его здесь правильно. Я добавлю его к исходному вопросу, как только он будет готов. –

+0

Лучше, если вы включили все хорошее из своих комментариев выше. – RBarryYoung

0

У меня был подобный вопрос и нашел две вещи, которые убили результаты запроса через ODBC

  • Граф промежуточных значений для обработки до окончательного вывода. Исправлено с «set nocount on» в начале запроса

  • нулевые значения, агрегированные прочь - запуск запроса непосредственно на SQL Server показал сообщение «Предупреждение: значение Null исключается агрегированием или другой операцией SET». Исправлено, преследуя каждую из них и заменяя пустые строки, нули или любое другое значение имеет смысл и не влияет на результаты запроса.

Я думаю, что в обоих случаях предупреждающее сообщение выводилось перед записями, поэтому набор записей хранилища был «заполнен» этим предупреждением, но никаких данных. В SQL Server эти предупреждения молча помещаются в журнал сообщений, но не влияют на результаты запроса, поэтому соблазн заключается в том, чтобы позволить им быть и перейти к работе с более высоким приоритетом.

 Смежные вопросы

  • Нет связанных вопросов^_^