IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Piloter Excel depuis Access : apprendre les bonnes pratiques
Un billet de blog de Denis Hulo

Le , par User

0PARTAGES

Ce billet s'adresse principalement aux utilisateurs d'Access qui ne connaissent pas très bien le modèle objet d'Excel et toutes les astuces nécessaires pour bien piloter le tableur.

On s'est tous retrouvés un jour à vouloir faire un export dans un fichier Excel avec des problèmes de lenteur, de fichier déjà ouvert ou de processus déjà chargé en mémoire, avec comme conséquence une procédure qui ne finit pas, ou carrément un plantage de l'application.

On va donc voir comment on peut contourner ces problèmes avec un exemple simple d'exportation d'une source de données Access dans un classeur.

Introduction :

On dispose d'une table ou d'une requête côté Access que l'on souhaite exporter dans un fichier Excel.
Pour une meilleure compréhension des choses, j'utilise du Early Binding qui nécessite de déclarer les variables objet dans un type précis, et de cocher la référence Microsoft Excel XX.X Object Library.

Les bonnes pratiques

  1. Bien déclarer les variables objet en début de code.
  2. Utiliser aussi une gestion d'erreur pour libérer la mémoire en cas d'erreur.
  3. Utiliser l'instance du classeur si celui-ci est déjà ouvert.
  4. Pour optimiser le temps d'exécution du code, désactiver en début de procédure le calcul automatique, le rafraîchissement de l'écran et les événements, puis les rétablir en fin de procédure.
  5. Bien libérer les variables objet en fin de procédure.


1/ - Déclarer les variables objet en début de code

Code VBA : Sélectionner tout
1
2
3
4
5
6
    Dim xlapp As Excel.Application ' variable Objet pour faire référence à l'application Excel
    Dim xlwbk As Excel.Workbook ' variable Objet pour faire référence au classeur
    Dim xlwsh As Excel.Worksheet ' variable Objet pour faire référence à la feuille
 
    Dim dbs As DAO.Database ' variable pour faire référence à la base courante
    Dim rst As DAO.Recordset ' variable pour faire référence au recordset lié à la table Access
2/ - Utiliser une gestion d'erreur

Elle va permettre en particulier de libérer la mémoire en cas d'erreur :

Code VBA : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
    On Error GoTo err_ExportData
    ...
    ...
err_ExportData:
 
    If Err.Number <> 0 Then ' si une erreur s'est produite
        MsgBox (Err.Description) ' affiche une description de l'erreur
    End If
 
    ' On libère les variables objet ici
3/ - Utiliser l'instance du fichier Excel si celui-ci est déjà ouvert

3.1/ - Création de l'objet application xlapp :

Code VBA : Sélectionner tout
1
2
3
4
5
6
    On Error Resume Next ' instruction pour ignorer l'erreur qui pourrait se déclencher sur la ligne suivante
    Set xlapp = GetObject(, "Excel.Application") ' on récupère l'instance Excel ouverte : si pas d'instance la variable est à nothing
    On Error GoTo err_ExportData ' reprend la gestion d'erreur normale en annulant l'effet de l'instruction On error resume next
    If xlapp Is Nothing Then ' si pas d'instance d'Excel de créée
        Set xlapp = CreateObject("Excel.Application") ' on crée une nouvelle instance
    End If
3.2/ - Création de l'objet xlwbk pour le classeur

Le fichier est déjà ouvert, comment éviter les problèmes de conflit en écriture ?

Pour cela, on va utiliser une instruction qui va permettre de récupérer l'instance du classeur déjà ouvert :

Code VBA : Sélectionner tout
1
2
3
4
5
6
7
    On Error Resume Next ' instruction pour ignorer l'erreur qui pourrait se déclencher sur la ligne suivante
    Set xlwbk = xlapp.Workbooks.Item(ExtractFileName(cheminFichierDestination)) ' on récupère l'instance du classeur ouvert : si pas d'instance la variable est à nothing
    On Error GoTo err_ExportData ' reprend la gestion d'erreur normale en annulant l'effet de l'instruction On error resume next
 
    If xlwbk Is Nothing Then ' si pas d'instance du classeur de déjà créée
        Set xlwbk = xlapp.Workbooks.Open(cheminFichierDestination) ' on crée une nouvelle instance
    End If

4/ - Pour optimiser le temps d'exécution du code

  • Désactiver au début du code le calcul automatique pour éviter des recalculs si par exemple votre classeur possède des formules.
  • Désactiver aussi le rafraîchissement automatique de l'écran.
  • Désactiver provisoirement aussi les événements.

Code VBA : Sélectionner tout
1
2
3
    xlapp.Calculation = xlCalculationManual ' désactive les calculs automatiques sur le classeur
    xlapp.ScreenUpdating = False ' désactive le rafraîchissement automatique de l'écran
    xlapp.EnableEvents = False ' désactive les événements
Réactiver ces options à la fin du code :

Code VBA : Sélectionner tout
1
2
3
    xlapp.ScreenUpdating = True ' active le rafraîchissement automatique de l'écran
    xlapp.EnableEvents = True ' active les événements
    xlapp.Calculation = xlCalculationAutomatic ' active les calculs automatiques des formules
5/ - Libérer les variables objet en fin de procédure

Pour libérer la mémoire et éviter de se retrouver avec des processus "fantômes", il faut impérativement mettre ces variables à nothing

Code VBA : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
 ' libère et ferme les variables objet
    Set rst = Nothing
    Set dbs = Nothing
 
    Set xlwsh = Nothing
 
    xlwbk.Close True ' on ferme avec sauvegarde du classeur
    Set xlwbk = Nothing
 
    xlapp.Quit ' quitte Excel
    Set xlapp = Nothing
La procédure complète

Arguments

  • DataSource : source de données Access (table, requête ou SQL).
  • CheminFichierDestination : chemin du classeur.
  • nomFeuille : nom de la feuille Excel.
  • Ajout : argument optionnel (vrai : ajoute à la fin ; faux : écrase les anciennes données).

Code VBA : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
Public Sub ExportData(dataSource As String, cheminFichierDestination As String, nomFeuille As String, Optional ajout As Boolean = False)
    ' Procédure d'exportation des données de la source, dans le fichier et la feuille passés en arguments
    On Error GoTo err_ExportData
 
    Dim xlapp As Excel.Application ' variable Objet pour faire référence à l'application Excel
    Dim xlwbk As Excel.Workbook ' variable Objet pour faire référence au classeur
    Dim xlwsh As Excel.Worksheet ' variable Objet pour faire référence à la feuille
 
    Dim dbs As DAO.Database ' variable pour faire référence à la base courante
    Dim rst As DAO.Recordset ' variable pour faire référence au recordset lié à la table Access
 
    Dim idLigne As Long, idColonne As Long, idligneMax As Long, idColonneMax As Long
 
    If MsgBox("Souhaitez-vous exporter les données dans le fichier Excel ?", vbYesNo) = vbNo Then
        Exit Sub
    End If
 
    If Not IsFile(cheminFichierDestination) Then ' si le fichier n'existe pas on sort
        MsgBox "Fichier introuvable !", vbExclamation ' on signale que le fichier est introuvable
        Exit Sub ' on sort de la procédure
    End If
 
    Set dbs = CurrentDb
    Set rst = dbs.OpenRecordset(dataSource)
 
    On Error Resume Next ' instruction pour ignorer l'erreur qui pourrait se déclencher sur la ligne suivante
    Set xlapp = GetObject(, "Excel.Application") ' on récupère l'instance Excel ouverte : si pas d'instance la variable est à nothing
    On Error GoTo err_ExportData ' reprend la gestion d'erreur normale en annulant l'effet de l'instruction On error resume next
 
    If xlapp Is Nothing Then ' si pas d'instance d'Excel de créée
        Set xlapp = CreateObject("Excel.Application") ' on crée une nouvelle instance
    End If
 
    On Error Resume Next ' instruction pour ignorer l'erreur qui pourrait se déclencher sur la ligne suivante
    Set xlwbk = xlapp.Workbooks.Item(ExtractFileName(cheminFichierDestination)) ' on récupère l'instance du classeur ouvert : si pas d'instance la variable est à nothing
    On Error GoTo err_ExportData ' reprend la gestion d'erreur normale en annulant l'effet de l'instruction On error resume next
 
    If xlwbk Is Nothing Then ' si pas d'instance du classeur de créée
        Set xlwbk = xlapp.Workbooks.Open(cheminFichierDestination) ' on crée une nouvelle instance
    End If
 
    Set xlwsh = xlwbk.Sheets(nomFeuille) ' on référence la 1re feuille du classeur
 
    ' Commandes pour optimiser la procédure d'export
    xlapp.Calculation = xlCalculationManual ' désactive les calculs automatiques sur le classeur
    xlapp.ScreenUpdating = False ' désactive le rafraîchissement automatique de l'écran
    xlapp.EnableEvents = False ' désactive les événements
 
    idColonneMax = rst.Fields.Count
    idligneMax = xlwsh.Cells(xlwsh.Columns(1).Cells.Count, 1).End(xlUp).Row ' on considère la dernière ligne comme indice maxi
 
    If Not ajout Then ' si pas mode ajout on considère la 1re ligne comme indice maxi
        xlwsh.Range(xlwsh.Cells(1, 1), xlwsh.Cells(idligneMax, idColonneMax)).ClearContents ' efface les anciennes données
        idligneMax = 1 ' on considère la 1re ligne comme indice maxi
    End If
 
    idLigne = idligneMax + 1 ' indice de la 1re ligne vierge sur la feuille du classeur disponible pour l'ajout
 
    ' mise à jour des en-têtes dans la feuille sur la 1re ligne
    For idColonne = 1 To rst.Fields.Count
        xlwsh.Cells(1, idColonne).Value = rst.Fields(idColonne - 1).Name ' copie du nom du champ
    Next idColonne
 
    xlwsh.Range("A" & idLigne).CopyFromRecordset rst ' copie du contenu du recordset à partir de l'indice de la ligne
 
    MsgBox "Export réalisé avec succès !", vbExclamation
 
err_ExportData:
 
    If Err.Number <> 0 Then
        MsgBox (Err.Description)
    End If
 
    On Error Resume Next
 
    xlapp.ScreenUpdating = True ' active le rafraîchissement automatique de l'écran
    xlapp.EnableEvents = True ' active les événements
    xlapp.Calculation = xlCalculationAutomatic ' active les calculs automatiques des formules
 
    If Not (rst Is Nothing) Then
        rst.Close
    End If
 
    ' libère et ferme les variables objet
    Set rst = Nothing
    Set dbs = Nothing
 
    Set xlwsh = Nothing
 
    xlwbk.Close True ' on ferme avec sauvegarde du classeur
    Set xlwbk = Nothing
 
    xlapp.Quit ' quitte Excel
    Set xlapp = Nothing
 
End Sub
 
Function IsFile(ByVal fileName As String) As Boolean
     IsFile = (Dir(fileName) > "")
End Function
 
Public Function ExtractFileName(ByVal FilePath As String) As String
        ExtractFileName = Mid(FilePath, InStrRev(FilePath, "\") + 1)
End Function
On exporte simplement en passant à la procédure les bons arguments :

Code : Sélectionner tout
ExportData "select * from T_Article where Export=False order by DesignationArticle;", "c:\Documents\Articles.xlsx", "Feuil1", True
Vous avez lu gratuitement 12 091 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.

Une erreur dans cette actualité ? Signalez-nous-la !