ADODB EXCEL SQL CopyFromRecordset to slow - sql

I use VBA and ADODB Connection in Excel to realize VLOOKUP with SQL in attached code:
The Line "ThisWorkbook.Worksheets("result").Range("A1").CopyFromRecordset rs" takes to much time. Is there a way to use "INSERT INTO" in "strSQL" and skip "copyfromrecordset" instead to speedup the makro?
Sub vlookup()
Dim cn As Object
Dim rs As Object
Dim strConnection As String
Dim strSQL As String
Worksheets("result").UsedRange.Clear
Set cn = CreateObject("ADODB.CONNECTION")
strConnection = "DRIVER={MICROSOFT EXCEL DRIVER (*.xls, *.xlsx, *.xlsm, *.xlsb)}; DBQ=" & ThisWorkbook.FullName
With cn
.Open strConnection
strSQL = "SELECT table1$.*, table2$.price FROM [table1$] LEFT JOIN [table2$] ON table1$.animal = table2$.animal"
Set rs = CreateObject("ADODB.RECORDSET")
With rs
.Source = strSQL
.ActiveConnection = strConnection
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Open
ThisWorkbook.Worksheets("result").Range("A1").CopyFromRecordset rs
.Close
End With
End With
cn.Close
Set cn = Nothing
Set rs = Nothing
End Sub

Found this question while struggling with legacy code which had CopyFromRecordSet. In my experience, it is horribly slow for anything more than a trivial number of lines.
Try extracting the Recordset information into 2D Variant Array and set the Value property of the respective Excel Range. Chunk your data if you think memory is going to be a constraint. The solution is involved. You'll need to do the bounds checks and also transpose the data before setting the Value property. Full answer below (without chunking):
Sub CopyFromRecordset(rngStart As Range, rstData As Variant)
Dim buffer As Variant
Dim i As Long, j As Long
If InStr(TypeName(rstData), "RecordSet") > 0 Then
rstData.MoveLast
rstData.MoveFirst
buffer = rstData.GetRows(rstData.RecordCount)
ReDim buffer2(UBound(buffer, 2), UBound(buffer, 1)) As Variant
For i = 0 To UBound(buffer, 1)
For j = 0 To UBound(buffer, 2)
buffer2(j, i) = buffer(i, j)
Next j
Next i
rngStart.Application.Range(rngStart, rngStart.Offset(UBound(buffer2, 1), UBound(buffer2, 2))).Value = buffer2
Else
Debug.Print ("Expected RecordSet, found " & TypeName(rstData))
End If
End Sub
Sub vlookup()
Dim cn As Object
Dim rs As Object
Dim strConnection As String
Dim strSQL As String
Worksheets("result").UsedRange.Clear
Set cn = CreateObject("ADODB.CONNECTION")
strConnection = "DRIVER={MICROSOFT EXCEL DRIVER (*.xls, *.xlsx, *.xlsm, *.xlsb)}; DBQ=" & ThisWorkbook.FullName
With cn
.Open strConnection
strSQL = "SELECT table1$.*, table2$.price FROM [table1$] LEFT JOIN [table2$] ON table1$.animal = table2$.animal"
Set rs = CreateObject("ADODB.RECORDSET")
With rs
.Source = strSQL
.ActiveConnection = strConnection
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Open
CopyFromRecordset ThisWorkbook.Worksheets("result").Range("A1"), rs
.Close
End With
End With
cn.Close
Set cn = Nothing
Set rs = Nothing
End Sub

Related

'Application.Transpose(rs.GetRows)' type mismatch error Nº 13 in SQL/VBA code

I'm trying to export data from an Oracle Database through VBA, and I'm getting an error Nº 13 Type Mismatch at line:
mtxData = Application.Transpose(rs.GetRows)
below is my entire code
Sub start()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim mtxData As Variant
Dim strSQL As String
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
cn.Open ( _
"user ID =user" & _
";Password=password" & _
";data source=source" & _
";Provider=OraOLEDB.oracle")
rs.CursorType = adOpenForwardOnly
strSQL = ("SELECT * FROM table")
rs.Open strSQL, cn
mtxData = Application.Transpose(rs.GetRows)
ActiveSheet.Range("A1:K22") = mtxData
below is the result I was expecting...
You will get a type mismatch error from Transpose if the data you received via GetRows contains any null values.
There is, however, a better way to dump the data you have in a RecordSet into Excel: Simply use the method Range.CopyFromRecordSet. Advantage is you don't need the transpose, and you need to specify only the start cell.
Const connStr = "(Your connection String)"
Const sql = "(Your SQL)"
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Set cn = New ADODB.Connection
cn.Open connStr
Set rs = cn.Execute(sql)
With ActiveSheet
.UsedRange.Clear
.Range("A1").CopyFromRecordset rs
End With
If you need also the column names, try this:
With ActiveSheet
.UsedRange.Clear
Dim destRange As Range, colIndex As Long
Set destRange = .Range("A1")
' Write column names
For colIndex = 0 To rs.Fields.Count - 1
destRange.Offset(0, colIndex) = rs(colIndex).Name
Next colIndex
' Dump the data
destRange.Offset(1, 0).CopyFromRecordset rs
End With

SQL Query Returns empty Recordset

I'm trying to learn how to connect to a SQL Server DB from Excel DB. I've tried to reduce the code to dead simple to begin with. I've looked at several answers to related questions, however, I cannot figure out why this doesn't work. It executes all the way through. (The code shown here is somewhat anonymized.)
The query finds the database, because if the table name is invalid it throws an error. However it always returns record count = -1. I can eyeball the table in MSSMS and it has data. Same result for other tables in the DB.
Public Sub ADOtest1()
Dim Conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strConnString As String
strConnString = "Provider='SQLOLEDB'" & ";" & _
"Data Source='XXX-XPS\SQLEXPRESS'" & ";" & _
"Initial Catalog='XXXXX'" & ";" & _
"Integrated Security='SSPI'"
Set Conn = New ADODB.Connection
Conn.Open strConnString
' the query finds the DB, because if the table name is incorrect, it throws an error
strSQLString = "SELECT * from t300_XXXX"
Set rs = Conn.Execute(strSQLString)
wrkRecordCount = rs.RecordCount
'--- just some test breakpoints
If wrkRecordCount = -1 Then
a = "" '--- code keeps arriving here
Else
a = ""
End If
rs.Close
Conn.Close
End Sub
Answer from Srinika below worked:
Set rs = Conn.Execute(strSQLString)
rs.Close
rs.CursorLocation = adUseClient
rs.Open
I'll post two examples, so please refer.
First Example
Sub ExampleSQL()
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim strSQL As String
Set cnn = New ADODB.Connection
'Set the provider property to the OLE DB Provider for ODBC.
'cnn.Provider = "MSDASQL"
'cnn.Provider = "Microsoft.ACE.OLEDB.12.0"
'cnn.Provider = "MSOLAP"
'cnn.Provider = "SQLOLEDB.1"
' Open a connection using an ODBC DSN.
cnn.ConnectionString = "driver={SQL Server};" & _
"server=severname;uid=sa;pwd=password;database=test"
Set rs = New ADODB.Recordset
strSQL = "SELECT * FROM [your Table] "
rs.Open strSQL, cnn.ConnectionString, adOpenForwardOnly, adLockReadOnly, adCmdText
cnn.Open
If cnn.State = adStateOpen Then
Else
MsgBox "Sever is not connected!! "
Exit Sub
End If
If Not rs.EOF Then
With Ws
.Range("a4").CurrentRegion.ClearContents
For i = 0 To rs.Fields.Count - 1
.Cells(4, i + 1).Value = rs.Fields(i).Name
Next
.Range("a5").CopyFromRecordset rs
.Columns.AutoFit
End With
Else
MsgBox "No Data!!", vbCritical
End If
rs.Close
Set rs = Nothing
cnn.Close
Set cnn = Nothing
End Sub
Second Example
Sub getDataFromServer()
Dim con As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rs As New ADODB.Recordset
Dim i As Integer
con.ConnectionString = "Provider=SQLOLEDB.1;" _
& "Server=(local);" _
& "Database=TEST;" _
& "Integrated Security=SSPI;" _
& "DataTypeCompatibility=80;"
con.Open
Set cmd.ActiveConnection = con
cmd.CommandText = "SELECT * FROM [your Table]"
Set rs = cmd.Execute
Range("A1").CopyFromRecordset rs
con.Close
Set con = Nothing
End Sub

Import MDB Table into EXCEL via vba - Need field names/headers

I have this bit of code that I found online that will import access records into excel. strFilePath is the filepath for the MDB and strTableName is the table name I am looking to import
Sub importAccessdata()
Dim cnn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sQRY As String
Dim strFilePath As String
strFilePath = Sheets("Setup").Range("C2").Value
strTableName = Sheets("Setup").Range("C4").Value
Set cnn = New ADODB.Connection
Set rs = New ADODB.Recordset
cnn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & strFilePath & ";"
sQRY = "SELECT * FROM " & strTableName & ""
rs.CursorLocation = adUseClient
rs.Open sQRY, cnn, adOpenStatic, adLockReadOnly
Application.ScreenUpdating = False
Sheet3.Range("A1").CopyFromRecordset rs
rs.Close
Set rs = Nothing
cnn.Close
Set cnn = Nothing
Exit Sub
End Sub
All of the records import very fast however I cannot get the field names to populate with them! Is there a simple modification I can do to carry the field names with the data?
This is what I use-
Private Sub PullSummaryData()
Const strDb As String = "C:\db\AccessDatabase.accdb"
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
Dim i As Integer
Sheets("Summary").Select
Const strQry As String = "SELECT * FROM [AccessDataTable]"
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strDb & ";"
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Open strQry
End With
With Sheets("Summary")
For i = 1 To rs.Fields.Count
.Cells(2,i).Value = rs.Fields(i-1).Name 'fields is a 0 based collection
Next i
.Range("A3").CopyFromRecordset rs
End With
rs.Close
cn.Close
End Sub

VBA "rowset does not support fetching backward" error with .cursertype adjusted

Using the following code I open a connection with SQL server and drop the results straight into an array. My problem is that I get an error for the rowcount, which I need to redim my array. The error I get is on the line indicated below and reads
"rowset does not support fetching backward"
All answers I can find suggest the cursor type as the problem, but I have changed this as far as I can tell. Apologies for the long code, I felt it best to leave the start in.
Function ConnectServer() As String()
'Working SQL Server connection
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
Dim strSqlQuery As String
Dim iCols As Long
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=wait;" & _
"Initial Catalog=what;" & _
"User Id=foo;" & _
"Password=bar;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
Set rs = New ADODB.Recordset
rs.CursorType = adOpenDynamic
rs.CursorLocation = adUseClient
conn.CommandTimeout = 50
' Open the connection and execute.
conn.Open sConnString
' Debug.Print strSqlQuery
Set rs = conn.Execute("SELECT DISTINCT a FROM b")
' Check we have data.
If Not rs.EOF Then
'*****************Problem here********************
rs.MoveLast
Debug.Print rs.RecordCount
'Read into array and cleanup...
End If
End Function
I don't believe this to be a duplicate of this question:
Rowset does not support scrolling backward
Because I already incorporated the answer into my code and the problem still persists.
The problem is caused by using conn.Execute to fill the recordset. Setting the recordset's activeconnection to the ADODB.Connection and using the recordset's open method will fix the issue.
Function ConnectServer() As String()
'Working SQL Server connection
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConnString As String
Dim strSqlQuery As String
Dim iCols As Long
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=wait;" & _
"Initial Catalog=what;" & _
"User Id=foo;" & _
"Password=bar;"
' Create the Connection and Recordset objects.
Set conn = New ADODB.Connection
conn.ConnectionString = sConnString
conn.Open
Set rs = New ADODB.Recordset
With rs
.ActiveConnection = conn
.CursorType = adOpenDynamic
.LockType = adLockOptimistic
.Source = "SELECT * FROM b"
.Open
End With
' Check we have data.
If Not rs.EOF Then
rs.MoveLast
Debug.Print rs.RecordCount
'Read into array and cleanup...
End If
End Function
You can use Recordset.GetRows() to fill the array. No need to dim it. GetRows Method (ADO)

run-time error '3704' operation is not allowed when the object is closed

Noob here. This vba code in excel is supposed to connect to sql 2008 using ADO, run the query and populate sheet1 in excel. Now, the heading error keeps popping up On this line
" Sheet1.Range("A1").CopyFromRecordset rst".
Sub Code1()
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim SqlQuery As String
Application.ScreenUpdating = False
Const stADO As String = "Provider=SQLOLEDB.1;Password=NOPWDHERE;Persist Security Info=True;User ID=sa;Initial Catalog=StockControl;Data Source= PCSMIS01"
Set wbBook = ActiveWorkbook
Set WsSheet = wbBook.Worksheets(1)
SqlQuery = " SELECT * FROm dbo.Site "
Set cnt = New ADODB.Connection
Set rst = New ADODB.Recordset
With cnt
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 0
Set rst = .Execute(SqlQuery)
End With
Sheet1.Range("A1").CopyFromRecordset rst
'With rst
' .ActiveConnection = cnt
' .Open SqlQuery
' Sheet1.Range("A1").CopyFromRecordset rst
' .Close
'End With
rst.Close
cnt.Close
Set rst = Nothing
Set cnt = Nothing
End Sub
I have also tried an alternative by using the piece of commented code to copy the data into the sheet.I have researched similar topics on the internet to the best of my ability, but i am more confused now. Please help.
Replace Sheet1 (it isn't referenced) by WsSheet :
Sub Code1()
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim SqlQuery As String
Application.ScreenUpdating = False
Const stADO As String = "Provider=SQLOLEDB.1;Password=NOPWDHERE;Persist Security Info=True;User ID=sa;Initial Catalog=StockControl;Data Source= PCSMIS01"
Set wbBook = ActiveWorkbook
Set WsSheet = wbBook.Worksheets(1)
SqlQuery = " SELECT * FROm dbo.Site "
Set cnt = New ADODB.Connection
Set rst = New ADODB.Recordset
With cnt
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 0
Set rst = .Execute(SqlQuery)
End With
WsSheet.Range("A1").CopyFromRecordset rst
'With rst
' .ActiveConnection = cnt
' .Open SqlQuery
' Sheet1.Range("A1").CopyFromRecordset rst
' .Close
'End With