Is there a way to get the number of rows of recordset without using the movelast method in VBA? If I try to use the movelast method, the function will stop as i am using the UDF.
Yes, but you have to open the Recordset using adOpenStatic or adOpenKeyset. Using adOpenStatic will pull the entire Recordset into memory, so it's not a good idea if your application doesn't need to process the entire recordset, you need to view changes made by other users, or if it's too big to fit into memory. On the other hand, processing the Recordset can be much faster because it doesn't have to hit the database for each record.
adOpenStatic example:
' dbConnection is an ADODB.Connection object
rs.Open "source", dbConnection, adOpenStatic
Debug.Print rs.RecordCount
adOpenKeyset example:
rs.Open "source", dbConnection, adOpenKeyset
' note: LockType adLockOptimistic may be required for particular databases
' rs.Open "source", dbConnection, adOpenKeyset, adLockOptimistic
Upon reviewing BzKnt's answer, it appears that another option to gain access to RecordCount is to set the CursorLocation to adUseClient.
Dim rs As New ADODB.Recordset
rs.CursorLocation = adUseClient
rs.Open "source", dbConnection
Also note that this is dependent on the database and driver you are using. Some of these methods may not work for all types of databases.
Article here http://www.geeksengine.com/article/recordcount-ado-recordset-vba.html explains the issue very clearly.
Use VBA to get the correct number of records in a Recordset object
Issue involves the cursor used.
In short add this after setting:
rs = New ADODB.Recordset
' Client-side cursor
rs.CursorLocation = adUseClient
In case anyone is interested in a working example:
'Query a closed excel workbook and assign a column to a array.
Sub TestADO()
Dim MeArr() As Variant
MeArr = ADOLoader ("C:\Closed_Workbook.xlsx", "Sheet1", "Column One")
Debug.Print LBound(MeArr), MeArr(LBound(MeArr))
Debug.Print UBound(MeArr), MeArr(UBound(MeArr))
End Sub
' SubIDCol is the column header
Function ADOLoader(strSourceFile As String, SheetName As String, SubIDCol As String) As Variant
Dim RowPlace, f As Integer
Dim cn As Object, rs As Object, sql As String
Dim ACount As Integer
Dim SubIDArray() As Variant
sql = "Select [" & SubIDCol & "] from [" & SheetName & "$]"
'---Connecting to the Data Source---
Set cn = CreateObject("ADODB.Connection")
With cn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=" & strSourceFile & ";" & "Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
.CursorLocation = adUseClient
.Open
Set rs = .Execute(sql)
End With
' If you wanted the Headers:
' For f = 0 To rs.Fields.Count - 1
' On Error Resume Next
' .Cells(r, c + f).Formula = rs.Fields(f).Name
' Debug.Print rs.Fields(f).Name
' On Error GoTo 0
' Next f
RowPlace = 0
ACount = rs.RecordCount - 1
ReDim SubIDArray(rs.RecordCount - 1)
On Error Resume Next
rs.MoveFirst
On Error GoTo 0
Do While Not rs.EOF
For f = 0 To rs.Fields.Count - 1
On Error Resume Next
SubIDArray(RowPlace) = rs.Fields(f).Value
RowPlace = RowPlace + 1
On Error GoTo 0
Next f
rs.MoveNext
Loop
'---Clean up---
rs.Close
cn.Close
Set cn = Nothing
Set rs = Nothing
Debug.Print "Lower bound of array = " & LBound(SubIDArray)
Debug.Print "Upper bound of array = " & UBound(SubIDArray)
ADOLoader = SubIDArray()
End Function
Related
I use VBA Word with Access, to create/store medical visit notes for Nursing homes.
The following is an example of how I get data out of Access (obviously picking up mid-Sub). This is populating a ComboBox in Word which gives me a list of all my patients, it is working great!
'....
Set Conn = New ADODB.Connection
Set rs = New ADODB.Recordset
strConn = ActiveDocument.CustomDocumentProperties("strConn").Value
Conn.Open (strConn)
qry = "SELECT * FROM tblPatientInfo ORDER BY LastName, Firstname"
rs.Open qry, Conn, adOpenKeyset
rs.MoveFirst
x = 0
While Not rs.EOF
F1.ComboDashPtList.AddItem
F1.ComboDashPtList.List(x, 0) = rs.Fields("LastName").Value & ""
F1.ComboDashPtList.List(x, 1) = rs.Fields("FirstName").Value & ""
F1.ComboDashPtList.List(x, 2) = Format(rs.Fields("DOB").Value, "MM\/dd\/yyyy") & ""
F1.ComboDashPtList.List(x, 3) = rs.Fields("MedNumber").Value & ""
rs.MoveNext
x = x + 1
Wend
rs.Close
Exit Sub`
'....
This is an example of how I send my data back to Access (again picking up mid-Sub).
` Set Conn = New ADODB.Connection
Set rs = New ADODB.Recordset
Conn.Open (strConn)
rs.Open strDBPtInfo, strConn, adOpenKeyset, adLockOptimistic, adCmdTable
rs.AddNew
rs!MedNumber = strMedNum
rs!LastName = strLastName
rs!Firstname = strFirstName
rs!DOB = dtDOB
rs.Update `
'....
Sometimes I need to completely overwrite or add to a specific field in a certain Access table. For years 'someone may have an allergy to penicillin, but suddenly they are also allergic to codeine, so that has to 'be updated. This is the approach I've taken but I keep getting an error:
'....
` Set Conn = New Connection
Set rs = New Recordset
Conn.Open (strConn)
strUpdateqry = "SELECT * FROM tblMedHxInfo WHERE MedNumber = " & Chr(34) & strMedNum & Chr(34) & ""
rs.Open strUpdateqry, strConn, adOpenKeyset
rs!Allergies = "Penicillin, Codeine"
rs.Update
If rs.State = 1 Then rs.Close
If Conn.State = 1 Then Conn.Close
Set rs = Nothing
Set Conn = Nothing`
....
This is the Error:
"Run-time error '3251': Current Recordset does not support updating.
This may ne a limitation of the provider, or of the selected locktype"
'Any help would be greatly appreciated!
'Thanks,
'Derek
'I've tried using the recordset to create a temporary table and then use that to update but it's getting over my head
Explicitly declare connection and recordset type. Then Set lines are not required.
Set the lock type argument.
Reference connection object not string variable for opening recordset.
Dim cn As ADODB.Connection, rs As ADODB.Recordset
cn.Open (strConn)
strUpdateqry = "SELECT * FROM tblMedHxInfo WHERE MedNumber = " & Chr(34) & strMedNum & Chr(34)
rs.Open strUpdateqry, cn, adOpenKeyset, adLockOptimistic
rs.Update "Allergies", "Penicillin, Codeine"
Or instead of opening recordset object for insert or update:
Dim cn As ADODB.Connection
cn.Open (strConn)
cn.Execute "UPDATE tblMedHxInfo SET Allergies = 'Penicillin, Codeine' WHERE MedNumber='" & strMedNum & "'"
It dose not work [rs.RecordCount] on recordset made by [new ADODB.Recordset].
But it can work [rs.RecordCount] on recordset made by [CreateObject("ADODB.Recordset")].
By the way, it also dosen't work [rs.fields([integer])] on recordset made by [CreateObject("ADODB.Recordset")].
But it can work on [rs.fields([integer])] on recordset made by [new ADODB.Recordset]
How can I use two source([rs.fields([integer])] and [rs.RecordCount]) on one recordset?
Sub all_column()
Call connectDB
Dim strSql As String
Dim Syntax As String
Dim recordsAffected As Long
Dim nameOfTable As String
Dim numberOfRecord As Integer
Dim i, next_i As Integer
Set rsTable = CreateObject("ADODB.Recordset")
rsTable.CursorLocation = adUseClient
'This is query'
strSql = "show tables"
'Set recordset by [CreateObject("ADODB.Recordset")]'
'just for return of [.RecordCount]'
'Because of [.RecordCount] dose not work on [new ADODB.Recordset]'
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = adUseClient
rs.Open strSql, cn, adOpenStatic
numberOfRecord = rs.RecordCount
rs.Close
'Set recordset by [New ADODB.Recordset]'
'because of [.Fields(0)] dose not work on [CreateObject("ADODB.Recordset")]'
Set rs = New ADODB.Recordset
rs.Open strSql, cn, adOpenStatic
i = 2
Do While rs.EOF = False
nameOfTable = rs.Fields(0).Value
strSql = "DESC " + nameOfTable + ";"
rsTable.Open strSql, cn, adLockReadOnly
next_i = i + rsTable.RecordCount
Sheets("sheet1").Range("b" + CStr(i)).CopyFromRecordset rsTable
Sheets("sheet1").Range("a" + CStr(i) + ":a" + CStr(next_i - 1)).Value = nameOfTable
i = next_i
rsTable.Close
rs.MoveNext
Loop
rs.Close
cn.Close
End Sub
ADO = Microsoft Active X Data Objects 6.1 Library
SQL = 5.7.14-google-log (Google)
EXCEL = Microsoft Excel 2016 MSO (16.0.10228.20080)
I am trying to create a VBA script that will pull the results from a View (SELECT * FROM view_name) from the RecordSet.Source property, but when attempted, my CloseConnection error handler keeps getting caught. I can get results from a table using a simple query like SELECT * FROM tbl_name with no issues.
Below is the code I am using. Note: my Const variable has the Provider and Database information removed.
I guess it really comes down to is it even possible to get results from a View like I would from a table?
Option Explicit
Const ConStrMSSQL As String = _
"Provider=provider_name;Database=database_name;Trusted_Connection=yes;"
Sub test()
Dim formConnect As ADODB.connection
Dim formData As ADODB.recordSet
Dim formField As ADODB.Field
Set formConnect = New ADODB.connection
Set formData = New ADODB.recordSet
formConnect.ConnectionString = ConStrMSSQL
formConnect.Open
On Error GoTo CloseConnection
With formData
.ActiveConnection = formConnect
.Source = "SELECT * FROM v_data_extract_658"
.LockType = adLockReadOnly
.CursorType = adOpenForwardOnly
.Open
End With
On Error GoTo CloseRecordset
Sheets("test").Range("A1").Select
For Each formField In formData.Fields
ActiveCell.Value = formField.Name
ActiveCell.Offset(0, 1).Select
Next formField
Sheets("test").Range("A2").CopyFromRecordset formData
On Error GoTo 0
CloseRecordset:
formData.Close
CloseConnection:
formConnect.Close
End Sub
This is the error message:
run-time error 2147467259 (80004005): unknown token received from SQL Server
I think the big issue here is that you haven't defined a Command Object.
I somewhat put this together "freehand" and for certain, didn't test it but it should get you to where you need to go.
Sub test()
On Error GoTo ErrorHandle:
Dim formConnect As ADODB.Connection
Set formConnect = New ADODB.Connection
formConnect.ConnectionString = ConStrMSSQL
Dim cmd As ADODB.Command
Set cmd = New ADODB.Command
formConnect.Open
With cmd
.ActiveConnection = formConnect
.CommandType = adCmdText
.CommandText = "SELECT * FROM v_data_extract_658"
.CommandTimeout = 30
End With
Dim formData As ADODB.Recordset
Set formData = New ADODB.Recordset
formData.Open cmd, , adOpenStatic, adLockReadOnly
Sheets("test").Range("A1").Select
Dim formField As ADODB.Field
For Each formField In formData.Fields
ActiveCell.value = formField.Name
ActiveCell.Offset(0, 1).Select
Next formField
Range("A2").CopyFromRecordset formData
On Error GoTo 0
Cleanup:
If Not formData Is Nothing Then
If formData.State <> adStateClosed Then formData.Close
Set formData = Nothing
End If
If Not formConnect Is Nothing Then
If formConnect.State <> adStateClosed Then formConnect.Close
Set formConnect = Nothing
End If
Set cmd = Nothing
Exit Sub
ErrorHandle:
MsgBox Err.Description
'Do whatever else is needed to respond to errors.
Resume Cleanup
End Sub
Using Excel & VBA to fetch dta from SLQ Server is quite easy (not always, but these days).
Sub ADOExcelSQLServer()
' Carl SQL Server Connection
'
' FOR THIS CODE TO WORK
' In VBE you need to go Tools References and check Microsoft Active X Data Objects 2.x library
'
Dim Cn As ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Server_Name = "EXCEL-PC\SQLEXPRESS" ' Enter your server name here
Database_Name = "NORTHWND" ' Enter your database name here
User_ID = "" ' enter your user ID here
Password = "" ' Enter your password here
SQLStr = "SELECT * FROM [Customers]" ' Enter your SQL here
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
rs.Open SQLStr, Cn, adOpenStatic
' Dump to spreadsheet
For iCols = 0 To rs.Fields.Count - 1
Worksheets("Sheet1").Cells(1, iCols + 1).Value = rs.Fields(iCols).Name
Next
With Worksheets("sheet1").Range("a2:z500") ' Enter your sheet name and range here
'.ClearContents
.CopyFromRecordset rs
End With
' Tidy up
rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
End Sub
As an aside, you can try this as well (please change to suit your specific setup/configuration)...
Sub Working2()
Dim con As Connection
Dim rst As Recordset
Dim strConn As String
Set con = New Connection
strConn = "EXCEL-PC\SQLEXPRESS;Database=Northwind;Trusted_Connection=True"
con.Open strConn
'Put a country name in Cell E1
Set rst = con.Execute("Exec dbo.MyOrders '" & ActiveSheet.Range("E1").Text & "'" & ActiveSheet.Range("E2").Text & "'")
'The total count of records is returned to Cell A5
ActiveSheet.Range("A5").CopyFromRecordset rst
rst.Close
con.Close
End Sub
Please see the link below for more details.
https://www.excel-sql-server.com/excel-sql-server-import-export-using-vba.htm#Excel%20Data%20Export%20to%20SQL%20Server%20Test%20Code
I’m using the NEXTRECORDSET method to view the records generated from a SQL stored procedure. The SP takes the user input and searches 3 tables in a SQL database and returns those records which contain the value(s) to Access. I'm able to successfully see the results in my debug (debug.print) window in Access, but not in the Listbox lstResults1 or all 3 listboxes of an Access form. The form procedure that calls the function and the function itself is below. I was able to successfully pass the resultset to the Listboxes (lstResults1, lstResults2, etc.) in the form by substituting the rstCompound statement in the Function with a loop for each Recordset (see third code sample) but it wasn't as clean and I was getting an "Object Variable or With Block Variable Not Set", every time one of the values I searched for was in the second or third tables:
PROCEDURE
Private Sub cmdRun_Click()
'On Error Resume Next
Dim strSQL As String
'Stored procedure + parameters called from form
strSQL = "Exec spSQL_SearchDatabase " & "'" & Me.txtTables & "'" & _
", " & "'%" & Me.txtSearchTerm & "%'"
OpenMyRecordset rstCompound, strSQL
Set Me.lstResults1.Recordset = rstCompound
'debug - view procedure
Me.lblQuery.Caption = strSQL
Me.Repaint
End Sub
FUNCTION
Public Function OpenMyRecordset(rstCompound As ADODB.Recordset, strSQL As String, _
Optional rrCursor As rrCursorType, _
Optional rrLock As rrLockType, Optional bolClientSide As Boolean) As ADODB.Recordset
If con.STATE = adStateClosed Then
con.ConnectionString = "ODBC;Driver={SQL Server};Server=vnysql;DSN=RecordsMgmt_SQLDB;UID=DMP;Trusted_Connection=Yes;DATABASE=RecordsManagementDB;"
con.Open
End If
Set rstCompound = New ADODB.Recordset
With rstCompound
.ActiveConnection = con
.CursorLocation = adUseClient
.CursorType = IIf((rrCursor = 0), adOpenDynamic, rrCursor)
.LockType = IIf((rrLock = 0), adLockOptimistic, rrLock)
.Open strSQL
End With
' Display results from each recordset
intCount = 1
Do Until rstCompound Is Nothing
Debug.Print "Contents of recordset #" & intCount
Do Until rstCompound.EOF
Debug.Print rstCompound.Fields(0), rstCompound.Fields(1)
rstCompound.MoveNext
Loop
Set rstCompound = rstCompound.NextRecordset
intCount = intCount + 1
Loop
End Function
Substituted Statement in Function
Set rs1 = New ADODB.Recordset
With rs1
.ActiveConnection = con
.CursorLocation = adUseClient
.CursorType = IIf((rrCursor = 0), adOpenDynamic, rrCursor)
.LockType = IIf((rrLock = 0), adLockOptimistic, rrLock)
.Open strSQL
End With
Do Until rs1.EOF
Debug.Print rs1.Fields(0), rs1.Fields(1)
rs1.MoveNext
Loop
Set rs2 = rs1.NextRecordset
Do Until rs1.EOF
Debug.Print rs2.Fields(0), rs2.Fields(1)
rs2.MoveNext
Loop
Set rs3 = rs2.NextRecordset
Do Until rs3.EOF
Debug.Print rs3.Fields(0), rs3.Fields(1)
rs3.MoveNext
Loop
The Function OpenMyRecordset never sets anything to return. It needs something like
Set OpenMyRecordset = rstCompound
Also, with all the debug statements rstCompound may be at EOF and have nothing to show.
I have a macro in Excel tied to a command button on one of my worksheets. When clicked, I'm trying to have the data from my worksheet "FeedSamples" be exported into an Access Database Table called "ImportedData".
Can anyone assist me? I've tried multiple examples from the net with no luck. This is what I have right now but keep receiving "Run-time error '3343': Unrecognized database format 'filePath\FeedSampleResults.accdb
Dim db As Database
Dim rs As Recordset
Dim r As Long
Set db = OpenDatabase("filePath\FeedSampleResults.accdb")
Set rs = db.OpenRecordset("ImportedData", dbOpenTable)
r = 2
Do While Len(Worksheets("FeedSamples").Range("A" & r).Formula) > 0
With rs
.AddNew
.Fields("REPTNO") = Worksheets("FeedSamples").Range("B" & r).value
.Update
End With
r = r + 1
Loop
rs.Close
Set rs = Nothing
db.Close
Set db = Nothing
Once I get this accomplished, I need to code to have the Access Table export the Data into a dBase file.
Here's the code using ADO. You need to set the full path of your access database in Data Source.
Sub ExcelToAccessAdo()
Dim cn As ADODB.Connection, rs As ADODB.Recordset, row As Long
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.ACE.OLEDB.12.0; " & _
"Data Source=filePath\FeedSampleResults.accdb;"
' open a recordset
Set rs = New ADODB.Recordset
rs.Open "ImportedData", cn, adOpenKeyset, adLockOptimistic, adCmdTable
row = 3 ' the start row in the worksheet
Do While Not IsEmpty(Worksheets("FeedSamples").Range("A" & row))
With rs
.AddNew ' create a new record
.Fields("REPTNO") = Worksheets("FeedSamples").Range("A" & row).Value
.Update
End With
row = row + 1
Loop
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub