Excel VBA checking if range of values exist in ODBC connected database - vba

I am having an issue (otherwise I wouldn't be here) :)
In Excel I have a list of item numbers that need to be checked if they already exist in a database. At the moment the only way to do that is to run a query in a separate sheet to get all the item numbers (400,000+) which takes quite some time and has to be done each time.
I have started a vba query which goes through each cell that is selected and checks that value to see if it exist in the database. If it exist, the cell turns Red. If it doesn't exist, the cell turns green.
I'm not that great when it comes to querying databases using VBA so I used bits and pieces that I found online.
When I go to test it, Excel crashes and closes so I can't determine where its going wrong.
Public Sub CheckItemNoExist()
Dim DB As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim c As Range
Dim ItemNum As Variant
Dim bFound As Boolean
DB.Open "DSN=DBNAME;UID=****;PWD=****;"
Set rs = DB.OpenRecordset("SELECT [Inventory.ITM_NO] FROM [OAUSER.Inventory] WHERE [Inventory.ITM_NO]=" & ItemNum & ";", dbOpenDynaset)
For Each c In ActiveSheet.Selection
ItemNum = c.Value
bFound = Not rs.EOF
If bFound Then
c.Interior.Color = RGB(255, 0, 0)
Else
c.Interior.Color = RGB(0, 255, 0)
End If
Next
rs.Close
Set rs = Nothing
DB.Close
Set DB = Nothing
End Sub
Thank you!
Matt

I suggest you use an IN in your where clause to bring over all the items in the selection, then filter the recordset for each item to see if it found it. I don't have your data setup, so I used a truck database that I have handy. You should be able to convert
Sub CheckTruckExists()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sSql As String
Dim aInClause() As String
Dim rCell As Range
Dim vaTrucks As Variant
Dim i As Long
'Open a connection to the database
Set cn = New ADODB.Connection
cn.Open sCONNECTIONSTRING
'Get an array of values from the selection
vaTrucks = Selection.Value
'Increase the size of the one-dimensional array to match
ReDim aInClause(LBound(vaTrucks, 1) To UBound(vaTrucks, 1))
'Fill a one-dim array from the two-dim array so we can Join it
For i = LBound(vaTrucks, 1) To UBound(vaTrucks, 1)
aInClause(i) = vaTrucks(i, 1)
Next i
'Build the SQL statement and execute it
sSql = "SELECT ReportTruck FROM qryTrucks WHERE ReportTruck IN ('" & Join(aInClause, "','") & "')"
Set rs = New Recordset
Set rs = cn.Execute(sSql)
'Loop through the selected cells
For Each rCell In Selection.Cells
'clear the filter, then refilter the recordset on the filtered value
rs.Filter = adFilterNone
rs.Filter = "ReportTruck = '" & rCell.Value & "'"
'If the filter returned zero records, it's eof
If rs.EOF Then
rCell.Interior.Color = RGB(255, 0, 0)
Else
rCell.Interior.Color = RGB(0, 255, 0)
End If
Next rCell
End Sub
My ReportTruck field is a String, so I have to enclose all the trucks in my IN clause with single quotes. If you're looking for something other than a String, modify the Join to accommodate that data type.
Also, there's no error checking to makes sure the Selection is actually a Range object or that it contains more than one cell - both are necessary to avoid an error.

Related

MS Access Query to find gaps in sequential numbers when numbers are stored in Short Text field

I have a table (tblParts) with a PartNumber field (Short Text) which stores 6 digit part numbers for parts belonging to several families. The families are denoted by the first 2 digits of the part number (00, 01, 02, etc).
(NOTE: I did not create this table and am not able to change it at this time)
I need to find gaps in the numbering in order to fill in unused part numbers. If I have a project starting that needs 6 consecutive part numbers in a specific family, I want to find the first unused number in the first gap of that size or greater within that family.
Here is a small subset of the data.
PartNumber
020001
020002
020003
020004
020005
020006
020007
020009
020010
020011
020012
020013
020014
020019
020101
If I needed a single number, the query should find 020008. If I needed 3 numbers, it should find 0200015 and if I needed 10 numbers it should find 020020.
My SQL knowledge is very limited but I am trying to learn. I realize this would be much easier if the information was stored properly but I have no control over it.
I once wrote an article on the subject:
Find and Generate Missing Values in an Access Table
but that will fill up any gap until all new numbers were established. So, that code will need an expansion with an outer loop to ensure juxtaposed numbers at all times.
Private Sub btnSearch_Click()
' Read table/query sequentially to
' record all missing IDs.
' Fill a ListBox with missing values.
' A reference to Microsoft DAO must be
' present.
' Define search table or query.
Const cstrTable As String = "Orders"
Const cstrField As String = "OrderID"
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim lst As ListBox
Dim col As Collection
Dim strSQL As String
Dim strList As String
Dim lngLast As Long
Dim lngNext As Long
Dim lngMiss As Long
strSQL = "Select " & cstrField & "" _
& " From " & cstrTable & _
& " Order By 1;"
Set lst = Me!lstMissing
Set col = New Collection
Set dbs = CurrentDb
Set rst = dbs.OpenRecordset(strSQL)
If rst.RecordCount = 0 Then
'The recordset is empty.
'Nothing to do.
Else
lngLast = rst(cstrField).Value
rst.MoveNext
While rst.EOF = False
lngNext = rst(cstrField).Value
For lngMiss = lngLast + 1 To _
lngNext - 1
col.Add (lngMiss)
Next
lngLast = lngNext
rst.MoveNext
Wend
'Generate next value in sequence.
'Discard if collecting only
'missing values.
col.Add (lngLast + 1)
End If
rst.Close
'Populate list box from collection.
For lngMiss = 1 To col.Count
If Len(strList) > 0 Then
strList = strList & ";"
End If
strList = strList & col(lngMiss)
Debug.Print col(lngMiss)
Next
lst.RowSource = strList
Debug.Print strList
Set rst = Nothing
Set dbs = Nothing
Set col = Nothing
Set lst = Nothing
End Sub

Outputting combobox values to Excel from Access

I am trying to output the value of a combobox field in Access, as a string, to an Excel worksheet.
I tried several solutions/hacks and get 445 errors.
The combobox is a dropdown list of communities or populations served by the member (e.g. Gay men, Aging populations, Trans*, People of Colour, Aboriginal Groups, Women, New Canadians, etc.). Several can be selected and there are some blank records.
Sub OutputSub()
' Define a recordset for the Table I am using
Dim myConnection As ADODB.Connection
Set myConnection = CurrentProject.Connection
Dim myRecordset As New ADODB.Recordset
myRecordset.ActiveConnection = myConnection
myRecordset.Open "MemberList", , adOpenStatic, adLockOptimistic
' Open Excel and make a worksheet for me
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Add
Set xlSheet = xlBook.Worksheets(1)
' Make Excel visible through the Application object.
xlSheet.Application.Visible = True
' Variables for each of the values I will need
Dim memCom As Variant, memServ As Variant, memLangs As Variant, memTot As Variant
Dim memNum As Integer
memNum = 2
xlSheet.Application.Cells(1, 4).Value = "Services"
'This loops through each of the records in the Table MemberList
myRecordset.MoveFirst
Do Until myRecordset.EOF()
memCom = myRecordset.Fields("Communities Served")
' This next line causes a 1004 error, application or object defined error
xlSheet.Application.Cells(memNum, 4).Value = memCom
'Debug.Print memCom, memServ, memLangs
memNum = memNum + 1
myRecordset.MoveNext
Loop
' Cleanup open files/variables, just in case
myRecordset.Close
Set myRecordset = Nothing
Set myConnection = Nothing
End Sub
My goal is an Excel sheet with the values much like if I exported the DB as an Excel file. There are three previous columns of information I need in specific formats (which I have working, so I cut them out).
From StackOverflow I have found little information on how to access the value of the combobox, and lots of tips on how to add fields to the box.
UPDATE: The runtime error 1004: Application defined or object defined error happens as noted in the code as a comment.
UPDATE 2: Further digging has yielded this from the Office Dev Center: https://social.msdn.microsoft.com/Forums/office/en-US/f5de518d-a2f0-41b8-bfd3-155052547ab5/export-of-combo-box-to-excel-with-values-access-2010?forum=accessdev.
I created a query that will output the info I need with the memName, but I'm lost as to how to make it part of this output.
If you step through your program what line causes the error - that's the first step to troubleshooting
What is the value coming out of the field "Communities Served" ? Is it a comma delimited string that you want split into multiple columns - or just to fill the once cell with whatever comes out - that's what it looks like to me?
In any case, get rid of your error by changing
xlSheet.Application.Cells(memNum, 4).Value = memCom
to
xlSheet.Cells(memNum, 4).Value = memCom
EDIT - memCom is coming back as a array Use Debug Watch to look at
how you need to access the value - probably even as simple as
memcon(0)
Thank you for the help from both #dbmitch and #Nathan_Sav, they helped me figure out what was wrong, so I could fix it.
For posterity sake, and for completion in case someone else has this issue, the solution was to make a query in Access for my multivalued combobox, and then make the value of that query into a recordset. Then I had to make a list of the values that the query returned, and then return that list into my excel sheet.
Here is my code:
Sub OutputSub()
' Define a recordset for the Table I am using
Dim myConnection As ADODB.Connection
Set myConnection = CurrentProject.Connection
Dim myRecordset As New ADODB.Recordset
myRecordset.ActiveConnection = myConnection
myRecordset.Open "MemberList", , adOpenStatic, adLockOptimistic
'Define a recordset for the query into my services
Dim myConnection2 As ADODB.Connection
Set myConnection2 = CurrentProject.Connection
Dim myServicesRecordset As New ADODB.Recordset
myServicesRecordset.ActiveConnection = myConnection2
myServicesRecordset.Open "SELECT MemberList.[Organization Name], MemberList.[Services Offered].Value FROM MemberList", , adOpenStatic, adLockOptimistic
' Open Excel and make a worksheet for me
Dim xlApp As Excel.Application
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
Set xlApp = CreateObject("Excel.Application")
Set xlBook = xlApp.Workbooks.Add
Set xlSheet = xlBook.Worksheets(1)
' Make Excel visible through the Application object.
xlSheet.Application.Visible = True
' Variables for each of the values I will need
Dim memName As String, memSite As Variant
Dim memSoc As Variant, memNameHOLD
Dim memServ As Variant, memServHOLD As Variant, memServName As Variant
'Variable for the line in the database, and start it counting at 2, so there is space for Column headers
' Then give the column headers values.
Dim memNum As Integer
memNum = 2
xlSheet.Application.Cells(1, 1).Value = "Member Name"
xlSheet.Application.Cells(1, 4).Value = "Services"
'This loops through each of the records in the Table MemberList
myRecordset.MoveFirst
Do Until myRecordset.EOF()
' Sets the value of the variables to the content of the relevant field
memName = myRecordset.Fields("Organization Name")
memSite = myRecordset.Fields("Website")
'If there is content in the website field, then make the Org Name a link, else leave it as is.
If Not IsNull(memSite) Then
memNameHOLD = "" & memName & ""
Else
memNameHOLD = memName
End If
'Collect the Services offered into a list for this memName
myServicesRecordset.MoveFirst
Do Until myServicesRecordset.EOF()
memServ = myServicesRecordset.Fields(1)
memServName = myServicesRecordset.Fields(0)
If memServName = memName Then
memServHOLD = memServHOLD & memServ & ", "
Else
memServHOLD = memServHOLD
End If
myServicesRecordset.MoveNext
memServ = ""
Loop
xlSheet.Application.Cells(memNum, 1).Value = memNameHOLD
xlSheet.Application.Cells(memNum, 3).Value = memRegion
xlSheet.Cells(memNum, 4).Value = "Services Offered: " & memServHOLD
memNum = memNum + 1
myRecordset.MoveNext
'Clear the hold value
memServHOLD = ""
Loop
' Cleanup open files/variables, just in case
myRecordset.Close
Set myRecordset = Nothing
Set myConnection = Nothing
End Sub
The keen eye may still be able to tell me why there is a , in the output if there were no values. But all things considered, that small comma is an acceptable error.

Run through a loop for more than 100,000 rows of data in two sheets in the same workbook

I currently have code to allow me to look through the rows with matching ID from Sheet 1 and Sheet 2. When both IDs match, Sheet 2 information will be pasted to the Sheet 1 rows with the same IDs. My code works on less than 1,000 rows and when I tested it gave results within a minute.
The problem is that when I tried to run it for 1,000,000 rows it keeps running and for more than 20 minutes and never stop running since then. I hope anyone could assist me in making changes to the code to allow me to do a loop and copy paste the information from Sheet 2 to Sheet 1 for 200,000 rows.
Sub Sample()
Dim tracker As Worksheet
Dim master As Worksheet
Dim cell As Range
Dim cellFound As Range
Dim OutPut As Long
Set tracker = Workbooks("test.xlsm").Sheets("Sheet1")
Set master = Workbooks("test.xlsm").Sheets("Sheet2")
Application.ScreenUpdating = False
For Each cell In master.Range("A2:A200000")
Set cellFound = tracker.Range("A5:A43000").Find(What:=cell.Value, LookIn:=xlValues, LookAt:=xlWhole)
If Not cellFound Is Nothing Then
matching value
cellFound.Offset(ColumnOffset:=1).Value2 = cell.Offset(ColumnOffset:=2).Value2
Else
End If
Set cellFound = Nothing
Debug.Print cell.Address
Next
Application.ScreenUpdating = True
OutPut = MsgBox("Update over!", vbOKOnly, "Update Status")
End Sub
Above is the code that I have for now.
Incorporating #paulbica's suggestion, this ran in several seconds for me.
Sub Sample()
Dim rngTracker As Range
Dim rngMaster As Range
Dim arrT, arrM
Dim dict As Object, r As Long, tmp
With Workbooks("test.xlsm")
Set rngTracker = .Sheets("Tracker").Range("A2:B43000")
Set rngMaster = .Sheets("Master").Range("A2:C200000")
End With
'get values in arrays
arrT = rngTracker.Value
arrM = rngMaster.Value
'load the dictionary
Set dict = CreateObject("scripting.dictionary")
For r = 1 To UBound(arrT, 1)
dict(arrT(r, 1)) = r
Next r
'map between the two arrays using the dictionary
For r = 1 To UBound(arrM, 1)
tmp = arrM(r, 1)
If dict.exists(tmp) Then
arrT(dict(tmp), 2) = arrM(r, 3)
End If
Next r
rngTracker.Value = arrT
End Sub
You could use the index of a Dictionary object and use its native indexing properties to perform the lokups. I'm not sure just how well that will perform in a data set of 200K records where a high report of failure was going to occur and you are showing at least a 78% failure rate (200K records to match and update 43K records).
Sub Sample3()
Dim tracker As Worksheet, master As Worksheet
Dim OutPut As Long
Dim v As Long, p As Long, vMASTER As Variant, vTRACKER As Variant, dMASTER As Object
Set tracker = Workbooks("test.xlsm").Sheets("Sheet1")
Set master = Workbooks("test.xlsm").Sheets("Sheet2")
Set dMASTER = CreateObject("Scripting.Dictionary")
Debug.Print Timer
'Application.ScreenUpdating = False '<~~no real need to do this if working in memory
With tracker
vTRACKER = .Range(.Cells(5, 2), .Cells(Rows.Count, 1).End(xlUp)).Value2
End With
With master
vMASTER = .Range(.Cells(2, 1), .Cells(Rows.Count, 3).End(xlUp)).Value2
For v = LBound(vMASTER, 1) To UBound(vMASTER, 1)
If Not dMASTER.exists(vMASTER(v, 1)) Then _
dMASTER.Add Key:=vMASTER(v, 1), Item:=vMASTER(v, 3)
Next v
End With
For v = LBound(vTRACKER, 1) To UBound(vTRACKER, 1)
If dMASTER.exists(vTRACKER(v, 1)) Then _
vTRACKER(v, 2) = dMASTER.Item(vTRACKER(v, 1))
Next v
With ThisWorkbook.Sheets("Sheet1") 'tracker
.Cells(5, 1).Resize(UBound(vTRACKER, 1), 2) = vTRACKER
End With
'Application.ScreenUpdating = True '<~~no real need to do this if working in memory
Debug.Print Timer
OutPut = MsgBox("Update over!", vbOKOnly, "Update Status")
dMASTER.RemoveAll: Set dMASTER = Nothing
Set tracker = Nothing
Set master = Nothing
End Sub
Once both ranges are mirrored into variant arrays, a dictionary is created in order to fully utilize its indexing properties for identification.
The above shows about a significant increase in efficiency over 200K records in master vs 43K records in tracker.
btw, I did use an .XLSB for this; not an .XLSM.
It might also be faster to use ADODB.
Dim filepath As String
Dim conn As New ADODB.Connection
Dim sql As String
filepath = "c:\path\to\excel\file\book.xlsx"
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=""" & filepath & """;" & _
"Extended Properties=""Excel 12.0;HDR=No"""
sql = _
"UPDATE [Sheet1$A2:B200000] AS master " & _
"INNER JOIN [Sheet2$] AS tracker ON master.F1 = tracker.F1 " & _
"SET master.F2 = tracker.F2"
.Execute sql
End With
This works with Office 2007. Office 2010 (I haven't tested on 2013) has a security measure that prevents updating Excel spreadsheets with an SQL statement. In this case you can either use the old Jet provider, which doesn't have this security measure. This provider doesn't support .xlsx, .xlsm or .xlsb files; only .xls.
With conn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=""" & filepath & """;" & _
"Extended Properties=""Excel 8.0;HDR=No"""
Alternatively, you can read the resulting data into a disconnected recordset and paste the recordset into the original worksheet:
Dim filepath As String
Dim conn As New ADODB.Connection
Dim sql As String
Dim rs As New ADODB.Recordset
filepath = "c:\path\to\excel\file\book.xlsx"
With conn
.Provider = "Microsoft.ACE.OLEDB.12.0"
.ConnectionString = "Data Source=""" & filepath & """;" & _
"Extended Properties=""Excel 12.0;HDR=No"""
sql = _
"SELECT master.F1, IIF(tracker.F1 Is Not Null, tracker.F2, master.F2) " & _
"FROM [Sheet1$A2:B200000] AS master " & _
"LEFT JOIN [Sheet2$] AS tracker ON master.F1 = tracker.F1 "
rs.CursorLocation = adUseClient
rs.Open sql, conn, adOpenForwardOnly, adLockReadOnly
conn.Close
End With
Workbooks.Open(filepath).Sheets("Sheet1").Cells(2, 1).CopyFromRecordset rs
If using CopyFromRecordset, keep in mind that there is no guarantee of the order in which the records are returned, which might be a problem if there is other data in the master worksheet besides columns A and B. To resolve this, you can include those other columns in the recordset as well. Alternatively, you can enforce the order of the records using an ORDER BY clause, and sort the data in the worksheet before you begin.

Filtering a Million Records to 100,000 from two different sources

I have a project where I want to automate the reporting of a stupidly large set of data in Excel VBA. Basically I have a 1,000,000+ record database that I want to pull ~100,000 records from. The only way I have the 100,000 items is in an excel sheet, and I can't dump it into the same database to filter or into a temp table on the same server.
Is there a way to treat the Excel spreadsheet values as a database and call it within the SQL query in the VBA? I'd prefer not to use a loop because the database response is already poor enough as it is.
Ideas?
Thanks.
Edit - My assumption of "looping being bad" is incorrect according to one comment. Is this true? Would looping through the ID's have to poll the database 100,000 times or does it treat it as a single data pull?
You can use ADODB if you want to query within your workbook using sql like syntax.
I have included a sub to as an example of how to do this. You could call said sub like below:
Call queryTable("select top 100000 * from [Sheet6$A1:AI31]", range("Sheet5!A1"))
This would query data located in the range Sheet6$A1:AI31 (the first row being the headers) and would dump the data starting with the top left cell being Sheet5!A1.
Sub queryTable(sqlStr As String, destination As Range)
Dim strFile As String
Dim stADO As String
Dim cnt As ADODB.Connection
Dim recordcount As Long
Dim fieldcount As Long
Dim resultrange As Range
Dim mydestination As Range
strFile = ThisWorkbook.FullName
'connection string may need to be altered a little bit depending on your excel version
stADO = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
Set cnt = New ADODB.Connection
'Running query
With cnt
.CursorLocation = adUseClient
.Open stADO
.CommandTimeout = 0
Set rst = .Execute(sqlStr)
End With
Set mydestination = destination.Cells(1, 1).Offset(1, 0)
'Copying data (not headers) to destination
mydestination.CopyFromRecordset rst
'Setting some important variables
recordcount = rst.recordcount
fieldcount = rst.Fields.Count
Set range_collection = Range(mydestination.Cells(1, 1).Offset(-1, 0), mydestination.Cells(1, 1).Offset(recordcount - 1, fieldcount - 1))
'Copying the headers
For i = 0 To fieldcount - 1
mydestination.Cells(1, 1).Offset(-1, i).value = rst.Fields(i).name
Next i
'Closing everything down
cnt.Close
Set cnt = Nothing
End Sub

Is there a native excel class which allows a range to be copied and sorts/filters applied?

I have a range I'd like to arbitrarily sort and filter using vba. I don't, however, want it to affect the worksheet. I'd like to essentially copy the range into some native class that supports filtering and sorting (so i don't have to reinvent the wheel) and use that class to return a result to calling code.
Are there any classes I can use to this end? ListObject looked the most promising but it appears to require being tied to a worksheet range to work properly.
You can use recordsets. Here are some notes:
'Reference: Microsost ActiveX n.n Object Library '
Dim rs As ADODB.Recordset
Dim cn As ADODB.Connection
'From: http://support.microsoft.com/kb/246335 '
strFile = Workbooks(1).FullName
strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 8.0;HDR=Yes;IMEX=1"";"
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open strCon
'Must have client-side cursor for sorting '
rs.CursorLocation = adUseClient
'Substitute a name range for [Sheet1$] '
'or include a range of cells : [Sheet1&A1:C7] '
strSQL = "SELECT * FROM [Sheet1$] " _
& "WHERE TransID>2 ORDER BY MyTime"
rs.Open strSQL, cn, 3, 3
rs.Filter = "TransID=3"
rs.Sort = "Mytime"
'Write out to another sheet '
Worksheets(2).Cells(2, 1).CopyFromRecordset rs
You may find this thread interesting: syncing two lists with VBA
If you'd like to read and parse complex sets of data you can use the Microsoft ActiveX Data Objects Recordset 2.8 Library. With this you can read your data into a recordset, then filter, sort, append, delete and pass it to other functions.
I regularly use this, because i often have to manipulate and display large datasets. If it's in a recordset i can use the same manipulation and presentation routines over and over again.
See Merge Excel Sheets.... for an example of throwing data into a recordset. After you have the data in a recordset then use r.filter = "ColumnA = 1", or r.sort = "ColumnC, ColumnA".
Turns out I can create a recordSet to do this. Unlike, Remou's answer though we don't have to invoke a heavy weight odbc process on our sheet.
The following function (adapted from Mark Nold's answer) will create a record set from the supplied range. It assumes column headers are in the first row of the supplied range. This can be made more robust but its a good starting spot
Function CreateRecordSet(rSource As range) As Recordset
' Constants
Const MAX_CHARS = 1200
' Declarations
Dim rs As Recordset
Dim c As Long
Dim r As Long
Dim colCount As Long
Dim rowCount As Long
Dim fldName As String
colCount = rSource.Columns.Count
rowCount = rSource.rows.Count
' Setup record set
Set rs = New Recordset
r = 1 ' assume first row contains headers
For c = 1 To colCount
fldName = rSource.Cells(r, c).Value
rs.Fields.Append fldName, adVarChar, MAX_CHARS
Next c
' Fill record set
rs.Open
r = 2 ' skip header row
For r = 2 To rowCount
rs.AddNew
Debug.Print "row "; r & " of " & rowCount & " created"
For c = 1 To colCount
rs.Fields(c - 1) = CStr(rSource.Cells(r, c).Value)
Debug.Print "-- row(" & r; "): added col " & c & " of " & colCount
Next c
Next r
Set CreateRecordSet = rs
End Function
Sub TestCreateRecordSet()
Dim r As range
Dim rs As Recordset
Set r = range("A1:B4")
Set rs = CreateRecordSet(r)
End Sub
You want to use a Range class (just like CasperOne says). Here's some example VBA code
Function SortAndFilter(rSource As Range) As Range
Dim rResult As Range
Dim vaTemp As Variant
Dim wsTemp As Worksheet
Dim wbTemp As Workbook
vaTemp = rSource.Value
Set wbTemp = Workbooks.Add
Set wsTemp = wbTemp.Sheets(1)
Set rResult = wsTemp.Range("A1").Resize(UBound(vaTemp, 1), UBound(vaTemp, 2))
rResult.Value = vaTemp
rResult.Sort rResult.Cells(1), xlDescending
Set SortAndFilter = rResult
End Function
Sub Testit()
Dim rTest As Range
Set rTest = SortAndFilter(Selection)
'Do stuff with your range object
rTest.Parent.Parent.Close False 'close temp workbook
End Sub
Why not copy the data to a new, hidden worksheet, perform your sort/filter there, and then copy the data back when done?