Outputting combobox values to Excel from Access - vba

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.

Related

Excel table to Access query connection, [Microsoft][ODBC Microsoft Access Drive] too few parameters. expected 1

I'm trying to create a table in Excel, which takes data from Access Query. I'm unable to find this query listed under Data->From Access. I'm using Data->From Other Sources -> From Data connection Wizard -> ODBC DSN. On final step it throws error [Microsoft][ODBC Microsoft Access Drive] too few parameters. expected 1.
I will not post full query at this moment, it is long
I will post subquery part (with some formatting) , that already throws this error. Can someone take a look and pinpoint where is the problem.
All queries I have work properly in Access. But I need the results export to Excel, as whole reporting VBA tool is there. (I know I can make SELECT INTO and create table, but it is not as elegant and simple to update) Thank you all for your time. Have a nice day
SELECT
Employees.PersNo,
Employees.Employee_name,
Employees.Reporting_Month,
Employees.Gender_Key,
Employees.Start_Date,
Employees.Business_Unit,
Employees.Position_ID,
Employees.Position,
Employees.Local_Band,
Employees.PS_Group,
Employees.Wage_Amount,
val(Employees.Bonus) AS [Bonus_%],
val([Employees].[Commissions_(%)]) AS [Commisions_%],
Employees.Wage_type, Employees.Wkhrs,
Q1.Business_Unit,
Q1.Position_ID,
Q1.Position,
Q1.Local_Band,
Q1.PS_Group,
Q1.Wage_Amount,
[Q1].[Bonus_%],
[Q1].[Commisions_%],
Employees.Wage_type,
Employees.Wkhrs,
Employees.Evid_Status
FROM Employees LEFT JOIN (SELECT
Dateadd("m",1,[Employees.Reporting_Month]) AS Reporting_Month,
Employees.PersNo,
Employees.Local_Band,
Employees.PS_Group,
Employees.Wage_Amount,
val(Employees.Bonus) AS [Bonus_%],
val([Employees].[Commissions_(%)]) AS [Commisions_%],
Employees.Wage_type, Employees.Wkhrs,
Employees.Business_Unit,
Employees.Position_ID,
Employees.Position,
Employees.Evid_Status
FROM Employees WHERE Employees.Evid_Status=1 ) AS Q1
ON (Employees.Reporting_Month = [Q1].[Reporting_Month]) AND (Employees.PersNo = [Q1].[PersNo])
WHERE Employees.Evid_Status=1;
Because Position is a reserved word in MS Accces, simply escape the word in both outer query and subquery with backticks or square brackets.
Interestingly, while the table alias qualifier works for reserved words inside the MSAccess.exe GUI program, external ODBC calls like from Excel may fail without escaping such reserved words:
SELECT
...
Employees.[Position],
...
SELECT
...
Employees.`Position`,
...
You can use Excel to query Access, like you see in the link below.
http://translate.google.pl/translate?js=n&prev=_t&hl=pl&ie=UTF-8&layout=2&eotf=1&sl=pl&tl=en&u=http%3A%2F%2Fafin.net%2FKsiazkaSQLwExcelu%2FGraficznyEdytorZapytanSqlNaPrzykladzieMsQuery.htm
Also, consider using a parameter query to do the export from Access to Excel.
Dim dbs As DAO.Database
Dim qdfTemp As DAO.QueryDef
Dim strSQL As String, strQDF As String
Set dbs = CurrentDb
' Replace NameOfTableOrQuery with the real name of the table or query,
' replace NameOfForm with the real name of the form, and replace
' ADateControlOnForm and AnotherDateControlOnForm with the real names
' of the controls on that form
strSQL = "SELECT NameOfTableOrQuery.* FROM NameOfTableOrQuery " & _
"WHERE NameOfTableOrQuery.FieldName >= " & _
Format(Forms!NameOfForm!ADateControlOnForm.Value,"\#mm\/dd\/yyyy\#") & _
" And NameOfTableOrQuery.FieldName <=" & _
Format(Forms!NameOfForm!AnotherDateControlOnForm.Value,"\#mm\/dd\/yyyy\#") & "';"
strQDF = "_TempQuery_"
Set qdfTemp = dbs.CreateQueryDef(strQDF, strSQL)
qdfTemp.Close
Set qdfTemp = Nothing
' Replace C:\MyFolderName\MyFileName.xls with the real path and filename for the
' EXCEL file that is to contain the exported data
DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, _
strQDF,"C:\MyFolderName\MyFileName.xls"
dbs.QueryDefs.Delete strQDF
dbs.Close
Set dbs = Nothing
Or...write data from a record set in Access to Excel.
Dim lngColumn As Long
Dim xlx As Object, xlw As Object, xls As Object, xlc As Object
Dim dbs As DAO.Database
Dim rst As DAO.Recordset
Dim blnEXCEL As Boolean, blnHeaderRow As Boolean
blnEXCEL = False
' Replace True with False if you do not want the first row of
' the worksheet to be a header row (the names of the fields
' from the recordset)
blnHeaderRow = True
' Establish an EXCEL application object
On Error Resume Next
Set xlx = GetObject(, "Excel.Application")
If Err.Number <> 0 Then
Set xlx = CreateObject("Excel.Application")
blnEXCEL = True
End If
Err.Clear
On Error GoTo 0
' Change True to False if you do not want the workbook to be
' visible when the code is running
xlx.Visible = True
' Replace C:\Filename.xls with the actual path and filename
' of the EXCEL file into which you will write the data
Set xlw = xlx.Workbooks.Open("C:\Filename.xls")
' Replace WorksheetName with the actual name of the worksheet
' in the EXCEL file
' (note that the worksheet must already be in the EXCEL file)
Set xls = xlw.Worksheets("WorksheetName")
' Replace A1 with the cell reference into which the first data value
' is to be written
Set xlc = xls.Range("A1") ' this is the first cell into which data go
Set dbs = CurrentDb()
' Replace QueryOrTableName with the real name of the table or query
' whose data are to be written into the worksheet
Set rst = dbs.OpenRecordset("QueryOrTableName", dbOpenDynaset, dbReadOnly)
If rst.EOF = False And rst.BOF = False Then
rst.MoveFirst
If blnHeaderRow = True Then
For lngColumn = 0 To rst.Fields.Count - 1
xlc.Offset(0, lngColumn).Value = rst.Fields(lngColumn).Name
Next lngColumn
Set xlc = xlc.Offset(1,0)
End If
' write data to worksheet
Do While rst.EOF = False
For lngColumn = 0 To rst.Fields.Count - 1
xlc.Offset(0, lngColumn).Value = rst.Fields(lngColumn).Value
Next lngColumn
rst.MoveNext
Set xlc = xlc.Offset(1,0)
Loop
End If
rst.Close
Set rst = Nothing
dbs.Close
Set dbs = Nothing
' Close the EXCEL file while saving the file, and clean up the EXCEL objects
Set xlc = Nothing
Set xls = Nothing
xlw.Close True ' close the EXCEL file and save the new data
Set xlw = Nothing
If blnEXCEL = True Then xlx.Quit
Set xlx = Nothing
Or, simply import the data from Access to Excel.
Sub ADOImportFromAccessTable(DBFullName As String, _
TableName As String, TargetRange As Range)
' Example: ADOImportFromAccessTable "C:\FolderName\DataBaseName.mdb", _
"TableName", Range("C1")
Dim cn As ADODB.Connection, rs As ADODB.Recordset, intColIndex As Integer
Set TargetRange = TargetRange.Cells(1, 1)
' open the database
Set cn = New ADODB.Connection
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & _
DBFullName & ";"
Set rs = New ADODB.Recordset
With rs
' open the recordset
.Open TableName, cn, adOpenStatic, adLockOptimistic, adCmdTable
' all records
'.Open "SELECT * FROM " & TableName & _
" WHERE [FieldName] = 'MyCriteria'", cn, , , adCmdText
' filter records
RS2WS rs, TargetRange ' write data from the recordset to the worksheet
' ' optional approach for Excel 2000 or later (RS2WS is not necessary)
' For intColIndex = 0 To rs.Fields.Count - 1 ' the field names
' TargetRange.Offset(0, intColIndex).Value = rs.Fields(intColIndex).Name
' Next
' TargetRange.Offset(1, 0).CopyFromRecordset rs ' the recordset data
End With
rs.Close
Set rs = Nothing
cn.Close
Set cn = Nothing
End Sub
Having the same error - linking Excel and Access.
After changing double quotes to single quotes the error "too few parameters. expected 1" was resolved. The sample of correct code.
AND all_clean.lastapp='Dial'

VBA - Return Results in Sheet 2 if Sheet 1 is full

I'm currently running a SQL stored procedure from an Excel Macro. The count of returned records exceeds the maximum rows for one sheet. How can I transfer the overflow results to a second sheet?
Sub Button1_Click()
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim par As String
Dim WSP1 As Worksheet
Set con = New ADODB.Connection
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
Application.DisplayStatusBar = True
Application.StatusBar = "Contacting SQL Server..."
' Remove any values in the cells where we
' want to put our Stored Procedure's results.
Dim rngRange As Range
Set rngRange = Range(Cells(8, 2), Cells(Rows.Count, 1)).EntireRow
rngRange.ClearContents
' Log into our SQL Server, and run the Stored Procedure
con.Open "Provider=SQLOLEDB;Data Source=67.09;Initial Catalog=TEST..."
cmd.ActiveConnection = con
Application.StatusBar = "Running stored procedure..."
cmd.CommandText = "SP_Billing"
Set rs = cmd.Execute(, , adCmdStoredProc)
' Copy the results to cell B7 on the first Worksheet
Set WSP1 = Worksheets(1)
WSP1.Activate
If rs.EOF = False Then WSP1.Cells(8, 2).CopyFromRecordset rs
rs.Close
Set rs = Nothing
Set cmd = Nothing
con.Close
Set con = Nothing
Application.StatusBar = "Data successfully updated."
End Sub
Just pass the MaxRows parameter to .CopyFromRecordset and loop until you hit EOF. Each call advances the cursor in the recordset, and the copy starts from the current cursor location. I'd extract it to a Sub something like...
Private Sub SplitRecordsToSheets(records As ADODB.Recordset, perSheet As Long)
Dim ws As Worksheet
Do While Not records.EOF
Set ws = Worksheets.Add
ws.Cells(8, 2).CopyFromRecordset records, perSheet
Loop
End Sub
...and then call it like this:
' Log into our SQL Server, and run the Stored Procedure
con.Open "Provider=SQLOLEDB;Data Source=67.09;Initial Catalog=TEST..."
cmd.ActiveConnection = con
Application.StatusBar = "Running stored procedure..."
cmd.CommandText = "SP_Billing"
Set rs = cmd.Execute(, , adCmdStoredProc)
SplitRecordsToSheets rs, ActiveSheet.Rows.Count - 8
If you require some custom handling while parsing through your RecordSet (such as switching pages once you have printed, say 100k rows), you can no longer use the Range.CopyFromRecordset method. Instead, you may have to iterate through the recordset yourself. Here is a small sample of how to do such a thing (without giving the whole puzzle away, of course:
Dim i_RowCount As Long
Dim a_PrintArray As Variant, rg_PrintRg As Range
Dim i_Col As Integer
Const i_MaxRows As Long = 100000
' I recommend filling everything into an Array first and then Printing the array to Excel'
' Using your existing variables also '
ReDim a_PrintArray( 1 to i_MaxRows, 1 to rs.Fields.Count )
Set sh_Current = WSP1
Do Until rs.EOF
i_RowCount = i_RowCount + 1
If i_RowCount > i_MaxRows Then 'If we hit the max, print what we have'
' Setting up the print range to match the array size '
Set rg_PrintRg = shCurrent.Cells(8, 2)
Set rg_PrintRg = Range(rg_PrintRg, rg_PrintRg.Offset(i_MaxRows - 1, rs.Fields.Count - 1))
rg_PrintRg = a_PrintArray ' Print the array into the range '
i_RowCount = 1
Set sh_Current = sh_Current.Next
ReDim a_PrintArray( 1 to i_MaxRows, 1 to rs.Fields.Count )
End If
For i_Col = 0 To rs.Fields.Count - 1
a_PrintArray(i_RowCount, i_Col) = rs.Fields(i_Col).Value
Next i_Col
rs.MoveNext
Loop
Please note this code snippit is for demonstration only. It has not been compiled and may not be optimal for your specific application. For more information on the Recordset object: https://msdn.microsoft.com/en-us/library/ms681510%28v=vs.85%29.aspx

Populate Excel userform listbox with query data - listbox is blank

I'm attempting to populate a listbox on a userform in an excel document from a query to SQL server, but the listbox is always blank.
I'm trying to get a list of locations to populate which I will use to define parameters for a follow on query.
Here is my code:
Option Explicit
Sub Populate_ListBox_From_SQL()
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim stDB As String, stConn As String, stSQL As String
Dim xlCalc As XlCalculation
Dim vaData As Variant
Dim k As Long
'set SQL connection and connection string
Set cnt = New ADODB.Connection
stConn = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DW;Data Source=use-rptdw-00;Use Procedure for Prepare=1;Auto Translate=True;" _
& "Packet Size=4096;Workstation ID=PI-L-C03RTRD;Use Encryption for Data=False;Tag with column collation when possible=False"
cnt.ConnectionString = stConn
'your SQL statement
stSQL = "SELECT ldesc FROM fin.location ORDER BY ldesc"
With cnt
.CursorLocation = adUseClient 'Necesary for creating disconnected recordset.
.Open stConn 'Open connection.
'Instantiate the Recordsetobject and execute the SQL-state.
Set rst = .Execute(stSQL)
End With
With rst
Set .ActiveConnection = Nothing 'Disconnect the recordset.
k = .Fields.Count
'Populate the array with the whole recordset.
vaData = .GetRows
End With
'Close the connection.
cnt.Close
'Manipulate the Listbox's properties and show the form.
With UserForm1
With .ComboBox1
.Clear
.BoundColumn = k
.List = Application.Transpose(vaData)
.ListIndex = -1
End With
.Show vbModeless
End With
'Release objects from memory.
Set rst = Nothing
Set cnt = Nothing
End Sub
Maybe I'm putting the code in the wrong place? I have it under the base VBA code for the userform.
Or maybe I need to set properties for the ListBox itself?
I'm pretty new to VBA so any help would be appreciated
Never mind. I figured it out. I just wasn't making an explicit call to the function. Although, now I get "run-time error '400': form already displayed; can't show modally" error.
Any idea how to stop that from happening?

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

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.

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?