To build a report, I compute some data with Access, then use a VBA macro to export several tables, then this macro launches Excel and runs another macro from within Excel to compile everything.
Everything is quite fast already, but the necessity to have an Excel Worksheet with VBA code in it in addition to my Access VBA bothers me. All the more I recently added a lot of new functions to my database, with the option of producing several reports, with a lot of customisation available on Access Forms.
It is getting somewhat complicated for me to navigate between these two files, so I tried to put my Excel code in Access, so my colleagues (who are the end users) would only have to keep 1 file up to date on their computer, and it would make things easier to debug too.
The report was built as expected, but the process was 5 times slower. I tried measuring time taken by each step, and the ratio remains the same (except for the part when a PowerPoint presentation gets built, at the end).
So I decided to try this useless piece of code :
Sub test()
Dim t As Double 'Starting time
t = Round(Timer)
Dim b As Workbook
Dim s As Worksheet
Dim i As Integer, j As Integer 'Loop variables
'Create workbook
Set b = Workbooks.Add
'Get worksheet
Set s = b.Sheets(1)
'Double Loop
For i = 1 To 100
For j = 1 To 100
s.Cells(i, j) = "Hi!" 'Write some useless comment
Next
Next
b.Close False 'Close without saving
'Message
MsgBox (Round(Timer) - t) \ 60 & "'" & Format((Round(Timer) - t) Mod 60, "00") & "''"
End Sub
I didn't put any ScreenUpdating=False, EnableEvents=False, etc. on purpose.
When running the code from Excel, it takes 2 to 3 seconds.
When running the code from Access, it takes from 7 to 10 seconds!
I understand that Access has to send instructions to another application, which may slow the process. So I tried asking Excel to run my script in another instance of Excel (so I created an object "Excel.Application" in which I put my workbook, exactly what Access does in the background), and I got the same performance as if I ran the code from Access.
Is there any way to speed this up ? An instruction to change the way the VB script communicates with the application object it creates maybe ?
Thank you all in advance for your support.
Edit: As requested by ASH, here is the full code I run in Access (I wont put my report code, as it is quite huge with a lot of subs, functions, custom classes, etc.). Comments are in french, but here is a summary:
The first script is a function that creates or opens an Excel Workbook, with a few optimisations (Excel invisible, no screen updating, no calculation, etc.)
The second script is a sub that closes an Excel Workbook, with options of saving/saving as, and discarding optimisations from the first function
The third script is analternative version of the test code I put before.
Now for the code itself:
Option Explicit
Function Ouvrir_Classeur_Excel(Optional Fichier As String, Optional Optimiser As Boolean = False) As Workbook
'*********************************** Descriptif
'Ouvre (ou crée) un classeur Excel en appliquant une optimisation si demandé par l'utilisateur.
'L'optimisation cache et désactive le rafraîchissement visuel d'Excel, les messages d'alerte, les événements et les calculs automatiques.
'------------------ Paramètres
'Fichier : Le chemin du classeur à ouvrir. S'il est vide, un classeur est créé
'Optimiser : Indique si les scripts d'optimisation doivent être exécutés. Par défaut, cette option est désactivée
'*********************************** Exécution
With Excel.Application
If Optimiser Then
'Excel invisible
.Visible = False
'Désactivation du rafraîchissement
.ScreenUpdating = False
'Désactivation des messages d'alerte
.DisplayAlerts = False
'Désactivation des événements
.EnableEvents = False
End If
'Ouverture/Création du classeur
If Fichier <> "" Then Set Ouvrir_Classeur_Excel = Workbooks.Open(Fichier) 'Si on a spécifié un fichier, il est ouvert
If Fichier = "" Then Set Ouvrir_Classeur_Excel = Workbooks.Add 'Si on n'a pas spécifié de fichier, on en crée un
'Désactivation des calculs automatiques
If Optimiser Then .Calculation = xlCalculationManual
End With
End Function
Sub Fermer_Classeur_Excel(Classeur As Workbook, Optional Enregistrer As Boolean = False, _
Optional Emplacement As String, Optional Fin_Optimisation As Boolean = False)
'*********************************** Descriptif
'Ferme le classeur Excel spécifié.
'Si l'utilisateur le demande, le classeur peut être enregistré, ou enregistré sous.
'Si l'utilisateur le demande, les optimisations appliquées par la fonction Ouvrir_Classeur_Excel peuvent être annulées.
'------------------ Paramètres
'Classeur : Le classeur à fermer
'Enregistrer : Indique si le classeur doit être enregistré. Par défaut, cette option est désactivée
'Emplacement : Indique l'emplacement où enregistrer le classeur. Si vide, l'enregistrement sera simple.
'Fin_Optimisation : Indique si les optimisations doivent être annulées. Par défaut, cette option est désactivée
'*********************************** Exécution
With Excel.Application
'Enregistrement du classeur
If Enregistrer Then
If Emplacement = "" Then Classeur.Save
If Emplacement <> "" Then Classeur.SaveAs Emplacement
End If
'Réactivation des calculs automatiques
If Fin_Optimisation Then .Calculation = xlCalculationAutomatic
'Fermeture du classeur
Classeur.Close False
If Fin_Optimisation Then
'Réactivation du rafraîchissement
.ScreenUpdating = True
'Réactivation des messages d'alerte
.DisplayAlerts = True
'Réactivation des événements
.EnableEvents = False
End If
End With
End Sub
Sub testA()
'------------Relève de l'heure de début
Dim tGlo As Double 'Heure de début d'exécution du script
Dim infoFin As String
tGlo = Round(Timer)
Dim x As Excel.Application
Dim c As Workbook
Dim f As Worksheet
Dim i As Integer, j As Integer
Set c = Ouvrir_Classeur_Excel(, True)
Set f = c.Sheets(1)
i = 1
Do Until i = 100
j = 1
Do Until j = 100
f.Cells(i, j) = "Coucou"
j = j + 1
Loop
i = i + 1
Loop
Fermer_Classeur_Excel c, , , True
'------------Message de fin
infoFin = infoFin & Chr(10) & Chr(10) & "DUREE DE TRAITEMENT : " & (Round(Timer) - tGlo) \ 60 & "'" & Format((Round(Timer) - tGlo) Mod 60, "00") & "''"
MsgBox infoFin, , title:="** FIN DU TRAITEMENT **"
End Sub
Like this, it takes 2 to 7 seconds (with Excel already running), which is huge compared to less than 1 in Excel with the same optimisations.
The issue being that it does not seem that important here, but another process that usually takes 40 seconds in Excel, took more than 3 minutes when I migrated it to Access. I have another one (not yet optimised) that takes more that 3:30 in Excel, I don't even dare trying to run it in Access.