VBA SQL: Runtime Error 3704, how can I fix this? - sql

I am trying to run a SQL Query through VBA. (I'm new to VBA)
P.s I have searched so much online but no solution has helped just yet.
I have copied the code from another excel (which works flawlessly) but my SQL Query involves temp tables and the other doesn't (this used to work on previous files that had temp tables). For some reason it just fails and I get the following error on the following line:
Error: 3704 Operation is not allowed when the object is closed'
' Check we have data for OrderIDs.
If Not rsfswdata.EOF Then
My full VBA code;
Dim conn As ADODB.Connection
Dim rsfswdata As ADODB.Recordset
Dim sConnString As String
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=server001;" & _
"Initial Catalog=Dev;" & _
"Integrated Security=SSPI;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
'To wait till the query finishes without generating error
conn.ConnectionTimeout = 0
'To wait till the query finishes without generating error
conn.CommandTimeout = 0
' Open the connection and execute.
conn.Open sConnString
Set rsfswdata = conn.Execute(Sheets("SQL Query").Range("A1").Value)
' Check we have data for OrderIDs.
If Not rsfswdata.EOF Then
' Transfer result.
Sheets("test").Cells(3, 2).CopyFromRecordset rsfswdata
' Close the recordset
rsfswdata.Close
Else
MsgBox "Error: No records returned.", vbCritical
End If
' Clean up
If CBool(conn.State And adStateOpen) Then conn.Close
Set conn = Nothing
Set rsfswdata = Nothing
End Sub

Related

Run Excel SQL on Existing Sheet - Invalid Sheet/Table

Goal: Run SQL against data in an existing Excel worksheet.
I'm running the following code on an existing Excel worksheet. All ADO connections are working but when I run the SQL statement, it tells me that my table is invalid. Should I be passing the name of the worksheet OR the name of the table? I have tried both. Nothing works.
It errors when opening the recordset ("rs.Open strSQL, cn")
When I use the name of the worksheet in the SQL, I receive the following:
'AG1' is not a valid name. make sure that it does not include invalid characters or punctuation and that is is not too long.
When I use the name of the table, I receive the following:
The Microsoft Access database engine could not find the object 'Table4'. Make sure the object exists and that you spell it's name and the path name correctly. If 'Table4' is not a local object, check your network connection or contact the server administrator.
Thank you in advance.
Sub testSQL()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSQL As String
Dim strCon As String
' Declare variables
Dim strFile: strFile = ThisWorkbook.FullName
' construct connection string
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
' create connection and recordset objects
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
' open connection
cn.Open strCon
' construct SQL query
' "AG1" is the name of the sheet
' I've tried "Table4" (name of table) without luck
strSQL = "SELECT * FROM [AG1$] where [Language] = 'Spanish';"
' execute SQL query
rs.Open strSQL, cn
' close connection
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
End Sub

connecting to sql server via vba/odbc

I'm not sure what is wrong with my code here, it is not throwing any errors and is compiling successfully. However, the recordset is not grabbing any data. Nothing is being pasted into the sheet. The query itself runs fine from command line/sqlserver. Do I need to add a dsn somewhere in the connection string?
Sub queryTest()
Dim connection As New ADODB.connection
Dim recordset As ADODB.recordset
Dim strSQL As New ADODB.Command
connection.Open "DRIVER={SQL Server};SERVER=xxx;" & _
"trusted_connection=yes;DATABASE=xxxx"
strSQL.ActiveConnection = connection
strSQL.CommandText = "SELECT TOP (50) [CalendarSK] ,[CalendarMonthSK] ,[CalendarDate] FROM [xxxx].[dbo].[tblCalendar]"
strSQL.CommandType = adCmdText
Set recordset = strSQL.Execute
Sheets("Sheet1").Range("a1").CopyFromRecordset recordset
recordset.Close
connection.Close
End Sub

Excel VBA - Get Data from SQL based of Range - Automation Error

When running the below code I keep getting an Automation error, for the life of me I can't figure out why. Can anyone shed some light?
When I use the debug it highlights the below;
rs.Open SQLStr, cn
I saw some references to
I've been tasked to get data from a SQL DB based off the values in Column A Row 3 onwards.
Example of Excel Sheet:
ITEM | QTY TO PICK | QTY ON ORDER | Column 2 | Column 3 etc
PART 1 | 5 | <Data will be populated here>
PART 2 | 12 | <Data will be populated here>
This code runs through a Command Button.
The data pulled from SQL will be populated starting in C3 onwards.
Private Sub CommandButton2_Click()
' Create a connection object.
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
' Provide the connection string.
Dim strConn As String
'Use the SQL Server OLE DB Provider.
strConn = "Provider=SQLOLEDB;"
'Connect to the Pubs database on the local server.
strConn = strConn & "server=<server name>;INITIAL CATALOG=<DB Name>;"
'Use an integrated login.
strConn = strConn & " INTEGRATED SECURITY=sspi;"
'Now open the connection.
cn.Open strConn
'
'
ActiveSheet.Range("C3:G10000").Clear ' clear out existing data
Dim ItemNumber As String
ItemNumber = Range("A3").Value
' Create a recordset object.
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
SQLStr = "Select * from vw_WorksOrder WHERE ITEMNO = " & ItemNumber & ""
rs.Open SQLStr, cn
' Copy the records into cell A1 on Sheet1.
Sheet4.Range("C3").CopyFromRecordset rs
' Tidy up
rs.Close
cn.Close
Set rs = Nothing
Set cn = Nothing
As #Zac points out with incorrect use of quotes which resolves issue, consider not using quotes or variable concatenation at all by employing the industry best practice of parameterization. ADO can parameterize SQL calls using its Command CreateParameter method.
See below example using your setup where a ? is used as placeholder in prepared statement, then a parameter is later appended defining its name, type, direction size, and value.
...
Dim cmd As New ADODB.Command
With cmd
.ActiveConnection = cn
.CommandText = "SELECT * FROM vw_WorksOrder WHERE ITEMNO = ?"
.CommandType = adCmdText
.Parameters.Append cmd.CreateParameter("itemparam", adVarChar, adParamInput, 255, ItemNumber)
End With
Dim rs As New ADODB.Recordset
Set rst = cmd.Execute
...
Also, another industry best practice is error and exception handling for runtime errors as AutomationError is not useful for debugging. And you want to release all Set objects regardless of error or not. In VBA, you can use the On Error handling to output more useful messages and release objects from memory accordingly.
Private Sub CommandButton2_Click()
On Error Goto ErrHandle
'...same code but without any Set obj = Nothing (since used in ExitHandle)
ExitHandle:
Set rs = Nothing
Set cmd = Nothing
Set cn = Nothing
Exit Sub
ErrHandle:
Msgbox Err.Number & " - " & Err.Description
Resume ExitHandle
End Sub

VBA Recordset doesn't return all fields

I just startied working with this database and I have a small problem.
So the main idea behind this is to use VBA to get needed information from database that I can use later on.
I am using ADO recordset and connect sting to connect to server. All is fine apart from one problem: when I am creating RecordSet by using SQL request it only returns one field when i know there should me more. At the moment I think that RecordSet is just grabbing first result and storing it in but looses anything else that should be there. Can you please help me.
Here is my code:
'Declare variables'
Dim objMyConn As ADODB.Connection
Dim objMyCmd As ADODB.Command
Dim objMyRecordset As ADODB.Recordset
Dim fldEach As ADODB.Field
Dim OrderNumber As Long
OrderNumber = 172783
Set objMyConn = New ADODB.Connection
Set objMyCmd = New ADODB.Command
Set objMyRecordset = New ADODB.Recordset
'Open Connection'
objMyConn.ConnectionString = "Provider=SQLOLEDB;Data Source=Local;" & _
"Initial Catalog=SQL_LIVE;"
objMyConn.Open
'Set and Excecute SQL Command'
Set objMyCmd.ActiveConnection = objMyConn
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
objMyCmd.CommandType = adCmdText
'Open Recordset'
Set objMyRecordset.Source = objMyCmd
objMyRecordset.Open
objMyRecordset.MoveFirst
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
At the moment Debug returns only one result when it should return two because there are two rows with the same OrderID.
The recordset only opens a single record at a time. You are iterating through all the fields in a single record. Not each record in the recordset.
If your query returns two records, you need to tell the Recordset to advance to the next one.
A query returns one recordset which has some number of records which have some number of fields.
You are iterating through the fields only for one record in the returned recordset.
You can do this with a few ways, but I generally do something like:
objMyRecordset.MoveFirst
Do
If Not objMyRecordset.EOF Then
debug.print "Record Opened - only returning 1 field due to SQL query"
For Each fldEach In objMyRecordset.Fields
Debug.Print fldEach.Value
Next
'this moves to the NEXT record in the recordset
objMyRecordset.MoveNext
Else
Exit Do
End If
Loop
Note that if you want to include more fields you will need to modify this line:
objMyCmd.CommandText = "SELECT fldImage FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
To include whatever additional fields you want returned.
In addition to the #enderland's answer, you can also have a disconnected RecordSet, that have all the values and fields ready for consumption. It's handy when you need to pass the data around or need to close the connection fast.
Here's a function that returns a disconnected RecordSet:
Function RunSQLReturnRS(sqlstmt, params())
On Error Resume next
' Create the ADO objects
Dim rs , cmd
Set rs = server.createobject("ADODB.Recordset")
Set cmd = server.createobject("ADODB.Command")
' Init the ADO objects & the stored proc parameters
cmd.ActiveConnection = GetConnectionString()
cmd.CommandText = sqlstmt
cmd.CommandType = adCmdText
cmd.CommandTimeout = 900 ' 15 minutos
collectParams cmd, params
' Execute the query for readonly
rs.CursorLocation = adUseClient
rs.Open cmd, , adOpenForwardOnly, adLockReadOnly
If err.number > 0 then
BuildErrorMessage()
exit function
end if
' Disconnect the recordset
Set cmd.ActiveConnection = Nothing
Set cmd = Nothing
Set rs.ActiveConnection = Nothing
' Return the resultant recordset
Set RunSQLReturnRS = rs
End Function
You are mixing up terms in your question which makes it unclear
In your first paragraph you describe a problem with "Fields", in the last paragraph you turn it into "Rows". Not exactly the same.
But whatever you are trying to achieve, the code you wrote will only return one field and one row.
If you want all FIELDS, your query should be:
objMyCmd.CommandText = "SELECT * FROM tblCustomisations WHERE fldOrderID=" & OrderNumber
If you want all ROWS, your loop should be:
objMyRecordset.MoveFirst
If Not objMyRecordset.BOF Then
While Not objMyRecordset.EOF
debug.print objMyRecordset!fldImage
RS.MoveNext
Wend
End If

Excel 2010 to Sql Server 2008 Insert Statment

I am going from Excel to Sql. I have the connection established. I can create a simple select statment and obtain values from a table in Sql into Excel. Now, I want to go the other way. I am trying to insert a value from excel into Sql. I keep getting a "Operation not allowed when object is closed" error # 3704. Below is my code.
Option Explicit
Private Conn As ADODB.Connection
Private Sub CommandButton1_Click()
Dim Conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
'This will create the string to connect.
sConnString = "Driver={SQL Server};Data Source=**;Initial Catalog = **;Trusted_Connection =yes;"
'Create Connection and the Recordset Objects.
Set Conn = New ADODB.Connection
Set rs = New ADODB.Recordset
'Open the Connection in Order to Execute.
Conn.Open sConnString
Set rs = Conn.Execute("insert into TestTable(TestColumn) Values('50');")
'Check for the Data.
If Not rs.EOF Then
Sheets(1).Range("A1").CopyFromRecordset rs
'Close Connection
rs.Close
Else
MsgBox "Error: No Records Returned.", vbCritical
End If
'Clean
If CBool(Conn.State And adStateOpen) Then Conn.Close
Set Conn = Nothing
Set rs = Nothing
End Sub
How do I properly execute this statement? As I said earlier the select statment worked fine. all I did was
("Select * From KpiSetupOee;")
Any thoughts? Thank you for your time
An INSERT statement doesn't return any records, so why are you trying to assign its results to a recordset? Change this line:
Set rs = Conn.Execute("insert into TestTable(TestColumn) Values('50');")
to just execute:
Conn.Execute("insert into TestTable(TestColumn) Values('50');")
Then clean up your code to get rid of unneeded recordset references.