Export to excel faster

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • svdoerga
    New Member
    • Jul 2008
    • 18

    Export to excel faster

    Hello!

    I currently have a button that exports a query, customized by the user on the form, to excel. The data that is exported is compared to older values (from a history table) and depending on the changes, the cellcolor is changed, so I need to format each cell individually. I have it working, but with many columns and more than 2500 records it can take like 15 minutes to export. Here's what I do:
    Code:
    'fill in data in excel cells
    intI = 2
    Do While Not rsCurLL.EOF()
        intCellCnt = 1
        For Each fd In rsCurLL.Fields
    	'get the IDLine of the current record
    	If intCurIDLine <> rsCurLL.Fields("IDLine").Value Then
    	    intCurIDLine = rsCurLL.Fields("IDLine").Value
    	    'move the rsOldLL pointer to the matching record
    	    rsOldLL.FindFirst ("[IDLine] = " & intCurIDLine & "")
    	    If rsOldLL.NoMatch Then
    		blnIDLineNotFound = True
    	    Else
    		blnIDLineNotFound = False
    	    End If
    	End If
    	'if the "Deleted" field is not in teh view query it will not be exported
    	If fd.Name = "Deleted" Then
    	    If varDeleted > 0 Then
    		xlSheet.Cells(intI, intCellCnt).Value = rsCurLL.Fields(fd.Name).Value
    		xlSheet.Cells(intI, intCellCnt).HorizontalAlignment = xlLeft
    		xlSheet.Cells(intI, intCellCnt).BorderAround xlContinuous
    		'if the current line is not present in the linelist_issued table, the line is new, so empty the old value
    		If blnIDLineNotFound = False Then
    		    strFValueOld = Nz(rsOldLL.Fields(fd.Name).Value)
    		Else
    		    strFValueOld = ""
    		End If
    		strFValueNew = Nz(rsCurLL.Fields(fd.Name).Value)
    		If strFValueNew <> strFValueOld Then
    		    xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(192, 192, 192)
    		End If
    		If rsCurLL.Fields("Deleted").Value = True Then
    		    xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(153, 204, 255)
    		End If
    		intCellCnt = intCellCnt + 1
    	    End If
    	'if the "IDLine" field is not in the view query it will not be exported
    	ElseIf fd.Name = "IDLine" Then
    	    If varIDLine > 0 Then
    		xlSheet.Cells(intI, intCellCnt).Value = rsCurLL.Fields(fd.Name).Value
    		xlSheet.Cells(intI, intCellCnt).HorizontalAlignment = xlLeft
    		xlSheet.Cells(intI, intCellCnt).BorderAround xlContinuous
    		If blnIDLineNotFound = False Then
    		    strFValueOld = Nz(rsOldLL.Fields(fd.Name).Value)
    		Else
    		    strFValueOld = ""
    		End If
    		strFValueNew = Nz(rsCurLL.Fields(fd.Name).Value)
    		If strFValueNew <> strFValueOld Then
    		    xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(192, 192, 192)
    		End If
    		If rsCurLL.Fields("Deleted").Value = True Then
    		    xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(153, 204, 255)
    		End If
    		intCellCnt = intCellCnt + 1
    	    End If
    	ElseIf fd.Name = "revision" Then
    	    'do nothing
    	Else
    	    xlSheet.Cells(intI, intCellCnt).Value = rsCurLL.Fields(fd.Name).Value
    	    xlSheet.Cells(intI, intCellCnt).HorizontalAlignment = xlLeft
    	    xlSheet.Cells(intI, intCellCnt).BorderAround xlContinuous
    	    If blnIDLineNotFound = False Then
    		strFValueOld = Nz(rsOldLL.Fields(fd.Name).Value)
    	    Else
    		strFValueOld = ""
    	    End If
    	    strFValueNew = Nz(rsCurLL.Fields(fd.Name).Value)
    	    If strFValueNew <> strFValueOld Then
    		xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(192, 192, 192)
    	    End If
    	    If rsCurLL.Fields("Deleted").Value = True Then
    		xlSheet.Cells(intI, intCellCnt).Interior.Color = RGB(153, 204, 255)
    	    End If
    	    intCellCnt = intCellCnt + 1
    	End If
        Next
        'display the current record
        intCurRecord = intCurRecord + 1
        lblCountCur.Caption = intCurRecord
        Forms("MainMenuNew").Repaint
        'move to next record
        rsCurLL.MoveNext
        intI = intI + 1
    Loop
    Is there a faster/more efficient way to do this? Any other tips are welcome since this is the first time I'm trying something like this.

    Thanks!
  • Stewart Ross
    Recognized Expert Moderator Specialist
    • Feb 2008
    • 2545

    #2
    Hi. The fastest way to export data to Excel will not work in your case, because you have so much cell formatting to do. Looking at your code I doubt there is much more you can do to it to speed it up unless you lose the formatting, as it is the formatting itself which is slowing things down.

    The fastest way to transfer data to Excel is to use a variant array to store the data, an array whose dimensions are reset to match the current number of rows and columns you need to transfer. Instead of accessing Excel directly, loop processing transfers data to the array, and then as a final step the array is assigned to Excel in one operation. This is much faster (around 10 times as fast) than using the Cells method to write data to Excel cell-by-cell.

    How I resolve it in cases such as yours is to open a pre-formatted Excel worksheet - a template sheet, although not an XLT file as such - and copy the data to the pre-formatted cells using the array method. I size the template to the maximum which the data will ever need, then delete any unused rows/columns within the processing of the worksheet after transferring the data from the array.

    This hybrid method works well for me, and may be something you wish to consider at a later date.

    -Stewart

    Code extract from one transfer routine, with use of dynamic array picked out
    Code:
    Dim RowData() As Variant '<< DYNAMIC ARRAY
    Dim RowRef As Long, ColRef As Long
    Dim CellRange As Excel.Range
    ' ...
    FirstPass = True
    RowCounter = 0
    Set RS = CurrentDb.OpenRecordset(ProgQueryName)
    Do Until RS.EOF
    	...
        If PrevDept <> DeptRef Then
    	If Not FirstPass Then
    	...
    	End If
    	FirstPass = False
    	ReDim RowData(MaxProgs, MaxCols) << SET UP ACTUAL DIMENSIONS
    	    ...
        End If
        RowData(RowCounter, 0) = CourseName ' << WRITE DATA TO THE ARRAY
        RowData(RowCounter, 1) = Block
        RowData(RowCounter, 2) = FEHE
        RowData(RowCounter, 4 + SessionOffset) = Enr
    	...
        PrevDept = DeptRef
        RS.MoveNext
    Loop
    RS.Close
    If Not FirstPass Then
        With xlRep.CurrentWorkSheet
    	Set CellRange = .Range(.Cells(StartRow, 2), .Cells(StartRow + RowCounter - 1, MaxCols + 1)) '<< SET UP THE CELL RANGE FOR THE ARRAY TRANSFER
        End With
        CellRange.Value = RowData '<< TRANSFER DATA FROM ARRAY TO EXCEL
        xlRep.DeleteRows RowCounter + StartRow, LastRow
    	...
    End If
    Last edited by Stewart Ross; Aug 3 '08, 08:10 AM. Reason: code sample added

    Comment

    • svdoerga
      New Member
      • Jul 2008
      • 18

      #3
      Originally posted by Stewart Ross Inverness
      Hi. The fastest way to export data to Excel will not work in your case, because you have so much cell formatting to do. Looking at your code I doubt there is much more you can do to it to speed it up unless you lose the formatting, as it is the formatting itself which is slowing things down.

      The fastest way to transfer data to Excel is to use a variant array to store the data, an array whose dimensions are reset to match the current number of rows and columns you need to transfer. Instead of accessing Excel directly, loop processing transfers data to the array, and then as a final step the array is assigned to Excel in one operation. This is much faster (around 10 times as fast) than using the Cells method to write data to Excel cell-by-cell.

      How I resolve it in cases such as yours is to open a pre-formatted Excel worksheet - a template sheet, although not an XLT file as such - and copy the data to the pre-formatted cells using the array method. I size the template to the maximum which the data will ever need, then delete any unused rows/columns within the processing of the worksheet after transferring the data from the array.

      This hybrid method works well for me, and may be something you wish to consider at a later date.

      -Stewart
      Thanks a lot for your reply Stewart. Too bad I can't use your method in my case, since I would lose the formatting. It is good to know there is such a fast way to export data to excel though. I should be able to use it in the future, thanks!

      Comment

      • NeoPa
        Recognized Expert Moderator MVP
        • Oct 2006
        • 32662

        #4
        Would a Copy/Paste not do the trick pretty efficiently?

        I'm planning that shortly in a current project.

        Comment

        • Stewart Ross
          Recognized Expert Moderator Specialist
          • Feb 2008
          • 2545

          #5
          Hi NeoPa. Copy/Paste does work - but it is tabular in format (so no ability to define where the data goes if you need a particular cell layout that is not just rectangular), is actually slower than other methods (the copy to the clipboard has to be complete before the data can be pasted at all), and the default paste overwrites any formatting. Although Paste Text can be used to resolve the formatting problem, it in turn can cause problems with fields formatted in certain ways (numerics formatted with leading zeros for example).

          In copying recordsets to Excel the Excel copy from recordset method is far faster than copy-paste. The use of a dynamic variant array is similarly fast, particularly for data that is not strictly tabular throughout.

          What really slows Excel automation down is any form of range or worksheet selection prior to copying a value, and using bespoke formatting a cell at a time. To take an instance, to form a border round a single cell means making at least four automation calls between Access and Excel with all the communication overheads that are entailed along with it. Range formatting is more efficient, because the range can be formatted in more or less the same number of communication calls as a single cell.

          -Stewart

          Comment

          • NeoPa
            Recognized Expert Moderator MVP
            • Oct 2006
            • 32662

            #6
            Thanks for all that Stewart.

            I always try to do my formatting by range where possible, and I have recently come across some of the problems associated with transferring data between cells in Excel. I tend to use the .Value = .Value approach where possible, but that tends to require formatting beforehand and afterwards.

            I will see what I can come up with in my project - then I may get back to you for some further clarification on any outstanding matters. Obviously in another thread, as I don't want to hijack this one any further than I have already :D

            Comment

            Working...