MS Access – VBA – Export RecordSet to Excel

Very similarly to my MS Access – VBA – Export Records to Excel post, I found myself needing to easily export a form’s recordset to Excel.  Not the underlying table or query, but the filtered, currently viewed recordset.  I already had the above code so I made a few very minor tweaks et voila I had a new function that could export, with ease, any recordset to Excel.  Hopefully it can help someone else!

'---------------------------------------------------------------------------------------
' Procedure : ExportRecordset2XLS
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Export the passed recordset to Excel
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' rs        : Recordset object to export to excel
'
' Usage:
' ~~~~~~
' Call ExportRecordset2XLS(Me.RecordsetClone)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-Mar-13             Initial Release
' 2         2018-09-20              Updated Copyright
'---------------------------------------------------------------------------------------
Function ExportRecordset2XLS(ByVal rs As DAO.Recordset)
    '#Const EarlyBind = True 'Use Early Binding, Req. Reference Library
    #Const EarlyBind = False    'Use Late Binding
    #If EarlyBind = True Then
        'Early Binding Declarations
        Dim oExcel            As Excel.Application
        Dim oExcelWrkBk       As Excel.WorkBook
        Dim oExcelWrSht       As Excel.WorkSheet
    #Else
        'Late Binding Declaration/Constants
        Dim oExcel            As Object
        Dim oExcelWrkBk       As Object
        Dim oExcelWrSht       As Object
        Const xlCenter = -4108
    #End If
    Dim bExcelOpened          As Boolean
    Dim iCols                 As Integer

    'Start Excel
    On Error Resume Next
    Set oExcel = GetObject(, "Excel.Application")    'Bind to existing instance of Excel

    If Err.Number <> 0 Then    'Could not get instance of Excel, so create a new one
        Err.Clear
        On Error GoTo Error_Handler
        Set oExcel = CreateObject("Excel.Application")
        bExcelOpened = False
    Else    'Excel was already running
        bExcelOpened = True
    End If
    On Error GoTo Error_Handler

    oExcel.ScreenUpdating = False
    oExcel.Visible = False   'Keep Excel hidden until we are done with our manipulation
    Set oExcelWrkBk = oExcel.Workbooks.Add()    'Start a new workbook
    Set oExcelWrSht = oExcelWrkBk.Sheets(1)

    With rs
        If .RecordCount <> 0 Then
            .MoveFirst    'This is req'd, had some strange behavior in certain instances without it!
            'Build our Header
            '****************
            For iCols = 0 To rs.Fields.Count - 1
                oExcelWrSht.Cells(1, iCols + 1).Value = rs.Fields(iCols).Name
            Next
            'Format the header
            With oExcelWrSht.Range(oExcelWrSht.Cells(1, 1), _
                                   oExcelWrSht.Cells(1, iCols))
                .Font.Bold = True
                .Font.ColorIndex = 2
                .Interior.ColorIndex = 1
                .HorizontalAlignment = xlCenter
            End With
            'Copy the data from our query into Excel
            '***************************************
            oExcelWrSht.Range("A2").CopyFromRecordset rs

            'Some formatting to make things pretty!
            '**************************************
            'Freeze pane
            oExcelWrSht.Rows("2:2").Select
            With oExcel.ActiveWindow
                .SplitColumn = 0
                .SplitRow = 1
                .FreezePanes = True
            End With
            'AutoFilter
            oExcelWrSht.Rows("1:1").AutoFilter
            'Fit the columns to the content
            oExcelWrSht.Range(oExcelWrSht.Cells(1, 1), _
                              oExcelWrSht.Cells(1, iCols)).EntireColumn.AutoFit
            'Start at the top
            oExcelWrSht.Range("A1").Select
        Else
            MsgBox "There are no records returned by the specified queries/SQL statement.", _
                   vbCritical + vbOKOnly, "No data to generate an Excel spreadsheet with"
            GoTo Error_Handler_Exit
        End If
    End With

Error_Handler_Exit:
    On Error Resume Next
    oExcel.Visible = True   'Make excel visible to the user
    Set rs = Nothing
    Set oExcelWrSht = Nothing
    Set oExcelWrkBk = Nothing
    oExcel.ScreenUpdating = True
    Set oExcel = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: ExportRecordset2XLS" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

As you can see by examining the code it includes Conditional Compiling Directive so you can have it either as Early or Late Binding to suit your preferences.

Furthermore, the following sections of code are completely optional and are simply used to perform some basic formatting (pretty things up and make the worksheet easier to work with IMHO). I’ve left it in place should it be useful to you and also to illustrate how easily you can perform other automations at the same time as performing the export (show some of the syntax). Feel free to remove it as you see fit.

            'Format the header
            With oExcelWrSht.Range(oExcelWrSht.Cells(1, 1), _
                                   oExcelWrSht.Cells(1, iCols))
                .Font.Bold = True
                .Font.ColorIndex = 2
                .Interior.ColorIndex = 1
                .HorizontalAlignment = xlCenter
            End With

and

            'Some formatting to make things pretty!
            '**************************************
            'Freeze pane
            oExcelWrSht.Rows("2:2").Select
            With oExcel.ActiveWindow
                .SplitColumn = 0
                .SplitRow = 1
                .FreezePanes = True
            End With
            'AutoFilter
            oExcelWrSht.Rows("1:1").AutoFilter
            'Fit the columns to the content
            oExcelWrSht.Range(oExcelWrSht.Cells(1, 1), _
                              oExcelWrSht.Cells(1, iCols)).EntireColumn.AutoFit

Taking Things Even Further :: ExportRecordset2XLS V2.0!

Now the above functions does exactly as intended, but what if we wanted more flexibility and more control over what is applied depending on the situation! This is the beauty of VBA once you truly get into it, you can develop some true coding gems that can be utilized in all sorts of situations.

So let reexamine the above function, how could we modify it to not need to actually remove sections of code depending on whether or not we want autofilters applies, or freeze panes, …? How could we make if flexible enough to even allow the user to specify an exist workbook to export to, and if none is specified then create a new one. How can we make a universal function?

Surprisingly, with a pretty small number of tweak to the above function we can do all that!

'---------------------------------------------------------------------------------------
' Procedure : ExportRecordset2XLS
' Author    : Daniel Pineault, CARDA Consultants Inc.
' Website   : http://www.cardaconsultants.com
' Purpose   : Export the passed recordset to Excel
' Copyright : The following is release as Attribution-ShareAlike 4.0 International
'             (CC BY-SA 4.0) - https://creativecommons.org/licenses/by-sa/4.0/
'
' Input Variables:
' ~~~~~~~~~~~~~~~~
' rs        : Recordset object to export to excel
' sFile     : Optional -> File path and name to update
'                   If none is provided a new Excel file is created
' sWrkSht   : Optional -> Name of the Worksheet to update
'                   If sWrkSht is supplied and the sheet does not exist it will be
'                   created
' lStartCol : Optional -> Column number to start inserting the data into
'                   If none is supply insert will be start on the 1st Column
' lStartRow : Optional -> Row number to start inserting the data into
'                   If none is supply insert will be start on the 1st Row
' bFitCols  : Optional -> Auto Fit the column to the width of the data contained within
'                   Default is True
' bFreezePanes : Optional -> Freeze the Header row
'                   Default is True
' bAutoFilter  : Optional -> AutoFilter the data
'                   Default is True
'
' Usage:
' ~~~~~~
' Call ExportRecordset2XLS(Me.RecordsetClone)
'
' Revision History:
' Rev       Date(yyyy/mm/dd)        Description
' **************************************************************************************
' 1         2017-Mar-13             Initial Release
' 2         2017-Mar-16             Added sFile
'                                   Added sWrkSht
'                                   Added lStartCol
'                                   Added lStartRow
'                                   Added bFitCols
'                                   Added bFreezePanes
'                                   Added bAutoFilter
' 2         2018-09-20              Updated Copyright
'---------------------------------------------------------------------------------------
Function ExportRecordset2XLS(ByVal rs As DAO.Recordset, _
                             Optional ByVal sFile As String, _
                             Optional ByVal sWrkSht As String, _
                             Optional ByVal lStartCol As Long = 1, _
                             Optional ByVal lStartRow As Long = 1, _
                             Optional bFitCols As Boolean = True, _
                             Optional bFreezePanes As Boolean = True, _
                             Optional bAutoFilter As Boolean = True)
    '#Const EarlyBind = True    'Use Early Binding, Req. Reference Library
    #Const EarlyBind = False    'Use Late Binding
    #If EarlyBind = True Then
        'Early Binding Declarations
        Dim oExcel            As Excel.Application
        Dim oExcelWrkBk       As Excel.WorkBook
        Dim oExcelWrkSht      As Excel.WorkSheet
    #Else
        'Late Binding Declaration/Constants
        Dim oExcel            As Object
        Dim oExcelWrkBk       As Object
        Dim oExcelWrkSht      As Object
        Const xlCenter = -4108
    #End If
    Dim bExcelOpened          As Boolean
    Dim iCols                 As Integer
    Dim lWrkBk                As Long

    'Start Excel
    On Error Resume Next
    Set oExcel = GetObject(, "Excel.Application")    'Bind to existing instance of Excel

    If Err.Number <> 0 Then    'Could not get instance of Excel, so create a new one
        Err.Clear
        On Error GoTo Error_Handler
        Set oExcel = CreateObject("Excel.Application")
        bExcelOpened = False
    Else    'Excel was already running
        bExcelOpened = True
    End If
    On Error GoTo Error_Handler

    oExcel.ScreenUpdating = False
    oExcel.Visible = False   'Keep Excel hidden until we are done with our manipulation

    If sFile <> "" Then
        Set oExcelWrkBk = oExcel.Workbooks.Open(sFile)    'Start a new workbook
        On Error Resume Next
        lWrkBk = Len(oExcelWrkBk.Sheets(sWrkSht).Name)
        If Err.Number <> 0 Then
            oExcelWrkBk.Worksheets.Add.Name = sWrkSht
            Err.Clear
        End If
        On Error GoTo Error_Handler
        Set oExcelWrkSht = oExcelWrkBk.Sheets(sWrkSht)
        oExcelWrkSht.Activate
    Else
        Set oExcelWrkBk = oExcel.Workbooks.Add()    'Start a new workbook
        Set oExcelWrkSht = oExcelWrkBk.Sheets(1)
        If sWrkSht <> "" Then
            oExcelWrkSht.Name = sWrkSht
        End If
    End If

    With rs
        If .RecordCount <> 0 Then
            .MoveFirst    'This is req'd, had some strange behavior in certain instances without it!
            'Build our Header
            '****************
            For iCols = 0 To rs.Fields.Count - 1
                oExcelWrkSht.Cells(lStartRow, lStartCol + iCols).Value = rs.Fields(iCols).Name
            Next
            'Format the header
            With oExcelWrkSht.Range(oExcelWrkSht.Cells(lStartRow, lStartCol), _
                                    oExcelWrkSht.Cells(lStartRow, lStartCol + iCols - 1))
                .Font.Bold = True
                .Font.ColorIndex = 2
                .Interior.ColorIndex = 1
                .HorizontalAlignment = xlCenter
            End With
            'Copy the data from our query into Excel
            '***************************************
            oExcelWrkSht.Cells(lStartRow + 1, lStartCol).CopyFromRecordset rs

            'Some formatting to make things pretty!
            '**************************************
            'Freeze pane
            If bFreezePanes = True Then
                oExcelWrkSht.Cells(lStartRow + 1, 1).Select
                oExcel.ActiveWindow.FreezePanes = True
            End If
            'AutoFilter
            If bAutoFilter = True Then
                oExcelWrkSht.Rows(lStartRow & ":" & lStartRow).AutoFilter
            End If
            'Fit the columns to the content
            If bFitCols = True Then
                oExcelWrkSht.Range(oExcelWrkSht.Cells(lStartRow, lStartCol), _
                                   oExcelWrkSht.Cells(lStartRow, lStartCol + iCols)).EntireColumn.AutoFit
            End If
            'Start at the top
            oExcelWrkSht.Cells(lStartRow, lStartCol).Select
        Else
            MsgBox "There are no records returned by the specified queries/SQL statement.", _
                   vbCritical + vbOKOnly, "No data to generate an Excel spreadsheet with"
            GoTo Error_Handler_Exit
        End If
    End With

Error_Handler_Exit:
    On Error Resume Next
    oExcel.Visible = True   'Make excel visible to the user
    Set rs = Nothing
    Set oExcelWrkSht = Nothing
    Set oExcelWrkBk = Nothing
    oExcel.ScreenUpdating = True
    Set oExcel = Nothing
    Exit Function

Error_Handler:
    MsgBox "The following error has occurred" & vbCrLf & vbCrLf & _
           "Error Number: " & Err.Number & vbCrLf & _
           "Error Source: ExportRecordset2XLS" & vbCrLf & _
           "Error Description: " & Err.Description & _
           Switch(Erl = 0, "", Erl <> 0, vbCrLf & "Line No: " & Erl) _
           , vbOKOnly + vbCritical, "An Error has Occurred!"
    Resume Error_Handler_Exit
End Function

Download

Feel free to download a copy by using the links provided below:

Disclaimer/Notes:

All code samples, download samples, links, ... on this site are provided 'AS IS'.

In no event will Devhut.net or CARDA Consultants Inc. be liable to the client/end-user or any third party for any damages, including any lost profits, lost savings or other incidental, consequential or special damages arising out of the operation of or inability to operate the software which CARDA Consultants Inc. has provided, even if CARDA Consultants Inc. has been advised of the possibility of such damages.

The YouTube Demo File:

Download “Access - Export to Excel” Export2Excel.zip – Downloaded 8290 times – 53.08 KB

The Original Article File:

As requested, feel free to download a fully function sample 2003 mdb of the above code which illustrates how it can be implemented.
Export Recordset To Excel (2K3 mdb)

21 responses on “MS Access – VBA – Export RecordSet to Excel

  1. Segolene Sansom

    Sorry I found the error, the Usage you put in the comments isn’t the right name compared to the actual function name: Export2XLS vs ExportRecordset2XLS

    The code works great by the way except for one thing. it doesn’t actually copy the data across… The header titles are all right and formatted but there’s no lines of data… Help?

  2. Bill

    I’m trying to use this code Function but having issues. I copied the Function to a ‘module’ – it compiled, no issues. I then added the following code to ‘Report_Open’ and it’s not working –
    Dim rst As DAO.Recordset
    ‘Set rst = Me.Recordset
    ‘Set rst = Me.Recordset.Clone
    (I TRIED EACH TO THE ABOVE (2) ‘SET’ STATEMENTS AND NEITHER WORK.
    Call ExportRecordset2XLS(rst)

    Any suggestions would be greatly appreciated and thanks for your help!

    1. Daniel Pineault Post author

      What happens exactly? Do you receive any error messages?

      I just quickly tested and place my code in a standard module and added a button to a random form and tried (using the button’s On Click event)

      Dim rs As DAO.Recordset
      Set rs = Me.RecordsetClone
      Call ExportRecordset2XLS(rs)
      Set rs = Nothing

      and

      Call ExportRecordset2XLS(Me.RecordsetClone)

      and both worked fine.

      That said, I’m confused by the choice of Event you are trying to use. Normally, you’d call this function from a command button that the user initiates, so the On Click event.

      Also, be sure that Excel didn’t open up in the background somewhere. You may need to use some Windows APIs to bring Excel to the forefront when you run this procedure (that’s what I do when I call any external program to ensure it gains the focus and isn’t hidden from the user).

  3. gerry

    Copied both your suggested functions and noon eo them get past this line
    Set oExcel = GetObject(, “Excel.Application”) ‘Bind to existing instance of Excel
    gives me a error message
    run time error 429 ActiveX component can’t create object
    any suggestions
    I went to references and added references to anything that resembled the words active X , script & excel

    1. Daniel Pineault Post author

      Gerry,

      Considering the line right above is a On Error Resume Next statement that extremely odd.

      What is your Error Handling Settings?
      Tools->Options->General->Error Trapping
      It should be set to Break on Unhandled Errors

      There is no need to for any References whatsoever. That is the whole point of using Late Binding!

      You do have Excel installed on your computer?

  4. gerry

    OMG…I changed the Break on all errors to either of the other 2 options and now I don’t get an Error abut active X…what on earth does this have to do with Active X 429…Microsoft developers need to get a grip with the error messages ..the ones that have no apparent reference to the real issue and as a result of that they throw you in a complete wrong direction.
    Thank for your help as I would never have solved this one…ever in a million years!!

  5. Bruce Williams

    Hi Daniel,
    Thanks for all your great Access help and code samples!

    Apparently I either don’t understand or passing the sFile info incorrectly. Based on the sFile input variable comment, I assumed if I passed a file path & name the routine would create a new Excel file and export the recordset data. But I am receiving an error when I provide the sFile info. I’m getting the following error info:
    Error Number = 1004
    Error Description = Sorry, we couldn’t find C:\TEMP\ qryMyTestQuery.xlsx. Is it possible it was moved, renamed or deleted?

    The error is on the following line:
    Set oExcelWrkBk = oExcel.Workbooks.Open(sFile) ‘Start a new workbook

    My test routine:
    Private Sub ExportMyData()
    Dim db As DAO.Database
    Dim rsQuery As DAO.Recordset

    Set db = CurrentDb
    Set rsQuery = db.OpenRecordset(“qryMyTestQuery”, dbOpenSnapshot)
    Call ExportRecordset2XLS(rsQuery, “C:\TEMP\qryMyTestQuery.xlsx”)

    Set rsQuery = Nothing
    Set db = Nothing
    End Sub

    Please help me understand how to correctly utilize the sFile feature.

    Again thanks for all your help!

    1. Daniel Pineault Post author

      Bruce,

      sFile is used to open/append an existing workbook. If you want a new workbbok to be created then you simply omit the sFile input variable.

      That said, I think I understand what you would like to happen, that is create a new file, export the data and save it as sFile. I will see what I can do.

      1. Bruce

        You are correct . My thought – if the file exists, then open/append the existing workbook. If the file does not exist, create a new file, export the recordset, and save the file.
        The Input Parameter notes implies this functionality:
        sFile Optional -> File path and name to update
        ‘If none is provided a new Excel file is created
        Thanks for all you help!

        1. Daniel Pineault Post author

          “If none is provided a new Excel file is created Thanks for all you help!”
          That’s exactly what it currently does. If you omit the sFile input variable then a new instance of Excel is created with a new blank workbook in which the data is imported. Then it is up to the user as to how/where to save it.

          That all said, I like your idea, and will see what I can do at a later date.

  6. Tarun Patel

    Hello Daniel,
    I am using your code to export the data to excel worksheet but i have one problem with Richtext.

    For ex.
    if I have column in database with datatype ‘LongText’ and TextFormat ‘Richtext’ than after exporting to excel it shows me a tags of html Like ‘div’ ‘br’ etc..

    How can i export to excel without any such tags?

    Can you help me on this.?

    Thanks for your input.

  7. Jean-Luc Prigent

    Hello Daniel,
    Great code, it saves me a lot of time and works great at the first try
    Will be embedded in a powershell applet (let’s play the fool)

    Merci beaucoup

  8. John

    Awesome and thank-you soooo much Daniel, saved a novice like me big time.
    Thanks for sharing this little bit of your IP as it is truly appreciated.

  9. Pierre Bastres

    Hello everyone,
    This function is extremely well made, and it also works with ADO, only one tiny tweak necessary: “ByVal rs As DAO.Recordset” -> “ByVal rs As ADODB.Recordset”.
    Many thanks Daniel.
    Pierre