Correction de "bug" sur macro VBA valeur cible
Bonjour à toutes et tous,
J'ai un premier code VBA qui me permet d'exécuter des calculs en utilisant la fonction valeur cible de Excel; ce code ci-dessous fonctionne très bien mais avec des temps d'exécution longs :
Sub VAM1()
'calculer VAM (valeur article moyen avec la fonction valeur cible de Excel)
Dim i As Integer
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
i = 0
With ThisWorkbook.Worksheets(4)
Do While .Cells(i + 30, 29).Value = "Panier calcul"
.Cells(i + 31, 29).GoalSeek Goal:=.Cells(i + 31, 24).Value, ChangingCell:=.Cells(i + 31, 33)
.Cells(i + 32, 29).GoalSeek Goal:=.Cells(i + 32, 24).Value, ChangingCell:=.Cells(i + 32, 33)
.Cells(i + 33, 29).GoalSeek Goal:=.Cells(i + 33, 24).Value, ChangingCell:=.Cells(i + 33, 33)
.Cells(i + 34, 29).GoalSeek Goal:=.Cells(i + 34, 24).Value, ChangingCell:=.Cells(i + 34, 33)
.Cells(i + 35, 29).GoalSeek Goal:=.Cells(i + 35, 24).Value, ChangingCell:=.Cells(i + 35, 33)
.Cells(i + 36, 29).GoalSeek Goal:=.Cells(i + 36, 24).Value, ChangingCell:=.Cells(i + 36, 33)
.Cells(i + 37, 29).GoalSeek Goal:=.Cells(i + 37, 24).Value, ChangingCell:=.Cells(i + 37, 33)
.Cells(i + 38, 29).GoalSeek Goal:=.Cells(i + 38, 24).Value, ChangingCell:=.Cells(i + 38, 33)
.Cells(i + 39, 29).GoalSeek Goal:=.Cells(i + 39, 24).Value, ChangingCell:=.Cells(i + 39, 33)
.Cells(i + 40, 29).GoalSeek Goal:=.Cells(i + 40, 24).Value, ChangingCell:=.Cells(i + 40, 33)
.Cells(i + 41, 29).GoalSeek Goal:=.Cells(i + 41, 24).Value, ChangingCell:=.Cells(i + 41, 33)
.Cells(i + 42, 29).GoalSeek Goal:=.Cells(i + 42, 24).Value, ChangingCell:=.Cells(i + 42, 33)
.Cells(i + 43, 29).GoalSeek Goal:=.Cells(i + 43, 24).Value, ChangingCell:=.Cells(i + 43, 33)
.Cells(i + 44, 29).GoalSeek Goal:=.Cells(i + 44, 24).Value, ChangingCell:=.Cells(i + 44, 33)
.Cells(i + 45, 29).GoalSeek Goal:=.Cells(i + 45, 24).Value, ChangingCell:=.Cells(i + 45, 33)
i = i + 23
Loop
End With
Calculate
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
Range("A1").Select
End Sub
J'ai essayé d'optimiser ce code pour réduire les temps d'exécutions avec le code ci dessous mais il bug sur cette ligne :
" cellsToChange(i).GoalSeek goalValues(i), .Cells(30 + (i - 1) * 23, 33) "
Le code approche optimisée :
Sub VAMOPTIMISER()
Dim i As Integer
Dim cellsToChange As Variant
Dim goalValues As Variant
Application.EnableEvents = False
Application.ScreenUpdating = False
timerDebut = Timer
With ThisWorkbook.Worksheets(4)
cellsToChange = Array(.Range("AI32:AI45"), .Range("AJ32:AJ45"), .Range("AK32:AK45"))
goalValues = Array(.Range("X32:X45").Value, .Range("Y32:Y45").Value, .Range("Z32:Z45").Value)
For i = 1 To UBound(cellsToChange)
cellsToChange(i).GoalSeek goalValues(i), .Cells(30 + (i - 1) * 23, 33)
Next i
End With
Calculate
Application.EnableEvents = True
Application.ScreenUpdating = True
Range("A1").Select
End Sub
Pourriez vous m'aider à corriger mon code et ou me proposer une autre piste d'optimisation ?
Merci beaucoup
Très cordialement
Hugues
Bonjour
J'ai essayé d'optimiser ce code pour réduire les temps d'exécutions avec le code ci dessous mais il bug sur cette ligne :
Premier code : vous mettez CALCULATE juste en dessous du END WITH alors que plus bas vous repassez en calcul automatique. Vous pouvez déjà essayer en supprimant cette instruction.
Dans votre deuxième code, rajouter l'instruction pour mise en calcul manuel puis sur automatique à la fin du code et supprimer le Calculate
re, ce sont des cellules, donc l'utilisation d'une matrice ne fonctionne pas.
Set CellsToChange = .Range("AI32:AK45")
' goalValues = Array(.Range("X32:X45").Value, .Range("Y32:Y45").Value, .Range("Z32:Z45").Value) '10 cellules vers gauche
For iC = 1 To CellsToChange.Columns.Count 'boucle les colonnes
For iL = 1 To CellsToChange.Rows.Count ' et les lignes
i = i + 1
MsgBox "CellTo change : " & .CellsToChange(i, j).Address & vbLf & "Valeur : " & .CellsToChange(i, j).Offset(, -10).Address & vbLf & "3ième cellule, i= " & i & vbLf & "ligne & colonne : " & .Cells(30 + (i - 1) * 23, 33).Address(1, 1, xlR1C1)
.CellsToChange(i, j).GoalSeek .CellsToChange(i, j).Offset(, -10).Value, .Cells(30 + (i - 1) * 23, 33)
Next
Next
Bonjour Dan, Bart, toutes et tous
Dan et Bart merci pour vos réponses.
En m'appuyant sur vos remarques et d'autres recherches j'ai pu construire le code ci dessous et également dans le fichier exemple joint.
La macro est fonctionnelle, je reviens vers vous pour savoir ce qui peut être optimisé.
Le temps d'exécution reste très long, à votre avis cela peut être du à la construction de mon code ou alors à la volumétrie des calculs à effectuer et de la complexité de la fonction valeur cible d'Excel en elle même.
Merci
Très cordialement
Hugues
Sub VAM()
'calculer VAM (valeur article moyen avec la fonction valeur cible de Excel)
' Panier calcul : cellule contenant la formule AC Cellule a definir
' Objectif Panier : cellule contenant la valeur que la formule doit atteindre X Valeur a atteindre
' VAM cellule contenant la variable a modifier AG Cellule a modifier
Dim timerDebut As Double
timerDebut = Timer
' Désactiver les fonctionnalités temporaires pour améliorer la performance
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
' Appel de la fonction personnalisée pour les différentes plages de lignes
PerformGoalSeek 31, 45
PerformGoalSeek 54, 68
PerformGoalSeek 77, 91
PerformGoalSeek 100, 114
PerformGoalSeek 123, 137
PerformGoalSeek 146, 160
PerformGoalSeek 169, 183
PerformGoalSeek 192, 206
PerformGoalSeek 215, 229
PerformGoalSeek 238, 252
PerformGoalSeek 261, 275
PerformGoalSeek 284, 298
' Réactiver les fonctionnalités après l'exécution du code
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
Range("A1").Select
MsgBox "Durée : " & (Timer - timerDebut) & " sec."
End Sub
' Fonction personnalisée pour appliquer GoalSeek sur une plage de lignes
Sub PerformGoalSeek(startRow As Long, endRow As Long)
Dim i As Long
Dim errorRows As String
For i = startRow To endRow
On Error Resume Next
Range("AC" & i).GoalSeek Goal:=Range("X" & i).Value, ChangingCell:=Range("AG" & i)
If Err.Number <> 0 Then
errorRows = errorRows & i & ", "
Err.Clear
End If
Next i
If Len(errorRows) > 0 Then
errorRows = Left(errorRows, Len(errorRows) - 2) ' Supprimer la virgule et l'espace en trop
MsgBox "Les lignes suivantes ont généré des erreurs lors de l'exécution de la fonction PerformGoalSeek: " & errorRows, vbExclamation
End If
End Sub
re,
ce sont les formules (indirect) des colonnes Q:R qui causent ce problème. Si vous videz ces colonnes, tout est presque immédiat.
Vous voulez faire quoi avec ces formules ?
Bonjour
Déjà deux instructions à supprimer --> Application.enableevents false et True. Elles ne servent à rien ici.
Après ce qui fait ralentir le code c'est la ligne ci-dessous :
Range("AC" & i).GoalSeek Goal:=Range("X" & i).Value, ChangingCell:=Range("AG" & i)
Cela prend déjà 35 sec rien que sur l'instruction "PerformGoalSeek 31, 45"
Faite du pas à pas avec les touches POMME + SHIFT + i (puisque je pense que vous êtes sous MAC), vous verrez directement
Crdlt
Edit @Bart
là c'est dingue !!!!. Je me demande comment excel voit cela sachant que l'on est en mode manuel dans le code. Si on se met en mode manuel et que l'on suspend l'instruction de remise en auto, la lenteur est identique.
Bonsoir Bart, et Dan, Toutes et tous,
Merci à vous Bart et Dan de continuez à suivre mon post et de me répondre.
Je vais de nouveau mettre en pratique vos conseils et vous en ferez un retour.
Bart, pour les indirect tel que INDIRECT("TABCA[CODE_MAG]")=$A8, dans le fichier complet cela permet de faire une recherche dans des données mises sous forme de tableau. Je passe par indirect pour que les références soit absolues et figées pour que lorsqu'on copie, colle, fait glisser etc. les formules soit figées sur certains de leur élément notamment celle faisant appel aux entêtes des données en tableau. Je ne connais pas d'autre moyen pour ce faire au même titre que l'on utilise $ sur les références de cellules. Mais peut être connaissez vous une autre approche. Je vais regarder pour ne plus faire appel à indirect en écrivant les formules pour chaque cellule (cela va certainement me demander un peu de temps, ou alors je vais réfléchir via une syntaxe en cherchant remplacer).
En tous cas déjà c'est mieux qu'au début et vous m'avez dans le bon sens forcé à réfléchir.
A suivre
Très cordialement
Hugues
Bonjour Dan, Bart, toutes et tous,
Je cloture ce post.
Merci Dan et Bart pour votre aide.
Je le mets comme résolu et Bart de nouveau je vous remercie car effectivement j'ai appliqué les conseils mutuels de Dan et Bart, mais la cause principale du ralentissement était bien l'utilisation de INDIRECT
A bientôt
Très cordialement
Hugues