VBA boucle while trop lente

Bonjour à tous,

Je débute en VBA et j'ai un problème avec une macro qui utilise une double boucle while mais qui met trop de temps à s'exécuter.

Voici ce que je demande de à ma fonction, j'ai une feuille avec plusieurs données (numéro de commande, référence, date de la commande, etc...) où sont remplis des commentaires.

Chaque semaine cette feuille est mis à jours avec les nouvelles commandes qui se sont ajoutées (les données proviennent d'une extraction du logiciel de commande) et je dois copier les commentaires de la semaine précédente dans la feuille de la semaine actuelle.

Je repère une commande avec identique d'une semaine à l'autre avec sa référence, sa quantité commandée, sa date de réception et son commentaire.

J'ai donc fait une première boucle while qui parcours la feuille de la semaine précédente puis une seconde qui parcours la feuille de la semaine actuelle pour copier les commentaires mais cette macro met trop de temps à s'éxécuter (environ 4 à 5 min).

Voici mon code :

'Récupération du nom du classeur de la semaine précédente pour pouvoir l'utiliser dans la suite de la fonction
Dim WB As Workbook
If Workbooks.Count = 2 Then
For i = 1 To 2
If Not Workbooks(i).Name = ThisWorkbook.Name Then Set WB = Workbooks(i)
Next i
End If

'Definition des variables
Dim feuillePrecedente As String, feuilleActuel As String

'Les noms des feuilles de la semaine actuelle (feuille "mise en forme") et de la semaine précédente sont récupérés
feuillePrecedente = WB.Sheets(1).Name
feuilleActuel = ThisWorkbook.Sheets("mise en forme").Name

    'Sélection de la 1ère cellule de la colonne Référence de la feuille de la semaine actuelle
    Dim CellReferenceF1 As Range
    Set CellReferenceF1 = ThisWorkbook.Worksheets(feuilleActuel).Range("C2")

    'Sélection de la 1ère cellule de la colonne quantité commandée de la feuille de la semaine actuelle
    Dim CellQuantiteF1 As Range
    Set CellQuantiteF1 = ThisWorkbook.Worksheets(feuilleActuel).Range("D2")

    'Sélection de la 1ère cellule de la colonne date de réception planifiée de la feuille de la semaine actuelle
    Dim CellDateRecepF1 As Range
    Set CellDateRecepF1 = ThisWorkbook.Worksheets(feuilleActuel).Range("H2")

    'Sélection de la 1ère cellule de la colonne commentaires de la feuille de la semaine actuelle
    Dim CellCommentaireF1 As Range
    Set CellCommentaireF1 = ThisWorkbook.Worksheets(feuilleActuel).Range("L2")

    'Sélection de la 1ère cellule de la colonne Référence de la feuille de la semaine précédente
    Dim CellReferenceF2 As Range
    Set CellReferenceF2 = WB.Worksheets(feuillePrecedente).Range("C8")

    'Sélection de la 1ère cellule de la colonne quantité commandée de la feuille de la semaine précédente
    Dim CellQuantiteF2 As Range
    Set CellQuantiteF2 = WB.Worksheets(feuillePrecedente).Range("D8")

    'Sélection de la 1ère cellule de la colonne date de réception planifiée de la feuille de la semaine précédente
    Dim CellDateRecepF2 As Range
    Set CellDateRecepF2 = WB.Worksheets(feuillePrecedente).Range("H8")

    'Sélection de la 1ère cellule de la colonne commentaires de la feuille de la semaine précédente
    Dim CellCommentaireF2 As Range
    Set CellCommentaireF2 = WB.Worksheets(feuillePrecedente).Range("L8")

    'Définition d'une variable qui va parcourir la feuille de la semaine précédente
    Dim CompteurF2 As Double
    CompteurF2 = 0

    'Définition d'une variable qui va parcourir la feuille de la semaine actuelle
    Dim CompteurF1 As Double

    'Définition de la variable booléenne "recopie"
    Dim Recopie As Boolean

    'Le premier while parcours la feuille de la semaine précédente et le second while parcours la feuille de la semaine actuelle
    While CellReferenceF2.Offset(CompteurF2, 0).Value <> ""
        CompteurF1 = 0
        Recopie = False
        While (CellReferenceF1.Offset(CompteurF1, 0).Value <> "") And (Recopie = False)

            ' Si la référence, la date de réception planifiée et la quantité commandée sont égales dans les 2 feuilles alors le commentaire de la semaine précédente et copié dans la feuille de la semaine actuelle
            If (CellReferenceF1.Offset(CompteurF1, 0).Value = CellReferenceF2.Offset(CompteurF2, 0).Value) And (CellQuantiteF1.Offset(CompteurF1, 0).Value = CellQuantiteF2.Offset(CompteurF2, 0).Value) And (CellDateRecepF1.Offset(CompteurF1, 0).Value = CellDateRecepF2.Offset(CompteurF2, 0).Value) Then
                CellCommentaireF1.Offset(CompteurF1, 0).Value = CellCommentaireF2.Offset(CompteurF2, 0).Value
                Recopie = True
            End If
            CompteurF1 = CompteurF1 + 1
        Wend
    CompteurF2 = CompteurF2 + 1
    Wend

Comme je débute en vba je pense qu'il est possible d'optimiser cette macro pour qu'elle soit beaucoup plus rapide mais je ne vois pas comment faire.

Quelqu'un pourrait-il m'aider svp ?

Je vous remercie d'avance

Bonjour,

si tu pouvais créer un fichier de quelques lignes qui représentent tous les cas de figure

Avec les 2 feuilles, et en précisant les colonnes clé et commentaire.

Précise aussi le nombre maxi de lignes. Selon la taille de la base plusieurs possibilités plus ou moins rapides (et donc plus ou moins simples)

eric

Bonjour eric

D'abord merci pour ton aide.

Je te joins deux fichiers.

Le fichier "exemple2" contient les commentaires que je veux copier et le fichier "exemple" est le fichier ou les commentaires doivent être copiés.

J'ai mis en jaune les colonnes que je parcours pour identifier les commandes identiques.

Le nombre de ligne augmente chaque semaine, sur le long terme le fichier pourra atteindre 500 000 lignes.


Je n'arrive pas à joindre les deux fichiers, quand je clique sur "Ajouter le fichier" il ne se passe rien

Ils sont plus gros que 300ko.

Ne mettre que qcq lignes représentant tous les cas, c'est suffisant.

Et quel est le format exact des noms de fichier ? En vba il faut être précis...

eric

C'est des fichiers .xlms et chaque fichier fait 92 Ko mais je n'arrive toujours pas à les joindres.

Existe t-il une autre solution ?

charles

Les zipper ou bien les déposer sur cjoint.com et coller ici le lien fourni.

Si tu rassembles les 2 feuilles dans le même fichier ça simplifie la macro.

eric

6exemple2.zip (61.56 Ko)
70exemple.zip (84.99 Ko)

Voilà les deux fichiers et je ne peux pas les mettre dans la même feuille.

Merci d'avance pour ton aide

Charles

Attention à WB.Sheets(1). Je l'ai laissé mais tu t'adresses à la feuille en position 1.

Utilise plutôt le nom de la feuille ou son codename qui ne dépendent pas de sa position.

Je me sers temporairement de la colonne M de 'QRY HA1_260514_formaté ', modifier si elle est susceptible de servir.

Au passage les espaces derrière les noms des feuilles sont à proscrire, trop piègieux...

Je n'ai pas trop testé vue l'heure.

Sub MiseAJour()
    'Definition des variables
    Dim shPrecedente As Worksheet, shActuel As Worksheet
    Dim WB As Workbook, i As Long
    Dim lig1 As Long, derlig1 As Long, lig2 As Long, derlig2 As Long
    Dim data1 As Variant, data2 As Variant, clé() As String
    Dim clé1 As String, c As Range
    Dim comment2() As String
    'Récupération du nom du classeur de la semaine précédente pour pouvoir l'utiliser dans la suite de la fonction
    If Workbooks.Count = 2 Then
        For i = 1 To 2
            If Not Workbooks(i).Name = ThisWorkbook.Name Then Set WB = Workbooks(i)
        Next i
    Else
        MsgBox "Pas 2 classeurs uniquement, abandon"
        Exit Sub
    End If

    'Les feuilles de la semaine actuelle (feuille "mise en forme") et de la semaine précédente sont récupérés
    Set shPrecedente = WB.Sheets(1)
    Set shActuel = ThisWorkbook.Sheets("mise en forme")

    'mise en mémoire des données
    derlig1 = shPrecedente.Cells(Rows.Count, 1).End(xlUp).Row
    data1 = shPrecedente.[A8].Resize(derlig1 - 8, 12)
    ReDim clé(1 To UBound(data1))
    derlig2 = shActuel.Cells(Rows.Count, 1).End(xlUp).Row
    data2 = shActuel.[A2].Resize(derlig1 - 1, 12)
    ReDim comment2(1 To UBound(data2))
    ' fabrication clés
    For lig1 = 1 To UBound(data1)
        clé(lig1) = data1(lig1, 3) & data1(lig1, 4) & data1(lig1, 8)
    Next lig1
    shPrecedente.[M8].Resize(UBound(data1)) = Application.Transpose(clé)
    ' recup commentaires
    For lig2 = 1 To UBound(data2)
        clé1 = data2(lig2, 3) & data2(lig2, 4) & data2(lig2, 8)
        Set c = shPrecedente.[M:M].Find(clé1, LookIn:=xlValues, lookat:=xlWhole)
        If Not c Is Nothing Then
            ' clé trouvée
            If Not IsEmpty(data1(c.Row - 7, 12)) Then
                comment2(lig2) = data1(c.Row - 7, 12)
                If data1(c.Row - 7, 12) = "annulée" Then shActuel.Rows(lig2+1).Font.Bold = True
            End If
        End If
    Next lig2

    shPrecedente.[M:M].ClearContents
    shActuel.[L2].Resize(UBound(comment2)) = Application.Transpose(comment2)
End Sub

eric

PS: je met en gras les lignes avec "annulée"

Merci bcp ça marche niquel la fonction s'éxécute en 2 seconde.

Par contre je t'avoue que je n'ai pas compris grand chose à ton code, je pense que tu utilises des tableaux mais je n'ai trop compris.

Peux-tu m'expliquer un peu plus en détail ?

Encore merci pour le temps que tu m'as accordé.

Charles

Bonjour,

Mettre les données dans des variables tableau est beaucoup plus rapide.

Déjà parce qu'on lit/écrit les données en un bloc, plutôt que d'effectuer de multiples lectures/écritures sur la feuille extrêmement chronophages. On travaille en mémoire.

J'ai commenté un peu plus le code.

A noter que j'ai changé la recherche de la clé.

J'avais fait le fainéant hier soir car il était un peu tard quand je m'y suis mis. Je collais les clés en M pour les rechercher facilement avec un .find() sur la feuille. Maintenant je fais une boucle en mémoire, 10 fois plus rapide pour cette partie.

J'ai mis les 2 feuilles dans le même classeur pour te montrer un problème.

Regarde les lignes 108-109 et leur équivalent 114-115 sur l'autre feuille, elles ont la même clé (les 3 champs) et ne peuvent être distinguées. Je récupère donc un commentaire à tort.

Faut-il ajouter un champ à la clé où est-ce une erreur dans le fichier de test, ce cas n'existant pas en réalité ?

(les clés mises en N ne sont là que pour faciliter le débogage et ne sont pas nécessaires)

Récupère et teste le nouveau code dans le fichier.

eric

20exemple.xlsm (181.40 Ko)

Merci pour ces précisions et oui je vais rajouter un champ à la clé pour éviter le problème que tu as repéré que je n'avais pas vu au passage.

Encore merci pour ton aide

charles

Rechercher des sujets similaires à "vba boucle while trop lente"