Actualisation de données d'un fichier à partir d'un autre

Bonjour,

En considérant les deux classeurs ci-joint, je souhaiterais que lorsqu'un changement de Status est effecuté dans le classeur D_Data, ce changement soit répercuté dans le classeur allData.

Comme son nom l'indique, le classeur allData contient un ensemble de données, et le classeur D_Data uniquement les données concernant la ressource D. Je précise que pour retrouver une ligne considérée dans D_Data dans allData, il faut considérer (vérifier) à la fois la Référence, le Nom Objet et le Type Objet.

Pourriez-vous me proposer une solution par macro svp ?

N'hésitez pas à me poser des questions si je n'ai pas été clair, ou si vous avez besoin de précisions.

Merci d'avance !

Edit: je précise que j'ai posté cette requête sur d'autres forums, j'espère que ça ne dérangera personne..

887alldata.xlsx (10.44 Ko)
918d-data.xlsm (9.71 Ko)

Bonjour m@tix,

Je ne sais pas si tu as trouvé la réponse à ta question, mais tu n'as pas forcément besoin de macro pour actualiser les données.

L'utilisation de formule peut suffire.

La lecture d'un classeur fermé peut se faire en mettant dans la cellule de destination concernée, l'adresse source exacte.

Un exemple dans ton cas :

En D7 de ton fichier allData, mets la formule suivante :

='C:\Users\*********\Desktop\excel-pratique\[D_Data.xlsm]Sheet1'!$D$2

Bien sûr le répertoire est à adapter.

On voit qu'il y a une référence directe à la cellule D2 de la feuille Sheet1 du fichier D_Data.xlsm qui se trouve dans le répertoire C:\Users\*********\Desktop\excel-pratique\

Lorsque tu ouvriras ton fichier allData, la mise à jour du status se fera automatiquement en fonction du status que l'on trouve dans le fichier D_Data.xlsm.

Bonjour vba-new !

Merci pour ta réponse. J'ai fait le test, et en effet ça fonctionne bien. Seulement, plusieurs raisons font que je souhaite vraiment passer par une macro. D'une part, je ne souhaite pas la mise à jour soit "automatique", je préfèrerais que l'utilisateur ait accès à un bouton (ou autre), pour effectuer la mise à jour sur l'autre fichier. D'autre part, avec ta méthode employant les références de cellule, cela peut-être problématique si des lignes sont ajoutées dans la liste.

De mon côté, j'ai essayé d'écrire quelques lignes de code pour parvenir à mes fins.

Sub update()

Data = ActiveWorkbook.Name

' Ouverture de "alldata"
Workbooks.OpenText Filename:="c:\Users\****\Desktop\alldata.xlsx", Origin:=xlWindows, _
        StartRow:=1, DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, _
        ConsecutiveDelimiter:=False, Tab:=True, Semicolon:=False, Comma:=False _
        , Space:=False, Other:=False

alldata = ActiveWorkbook.Name

lastrowA = Workbooks(alldata).Sheets("Sheet1").Cells.Find("*", ActiveSheet.Range("A1"), , , xlByRows, xlPrevious).Row
lastrowD = Workbooks(Data).Sheets("Sheet1").Cells.Find("*", ActiveSheet.Range("A1"), , , xlByRows, xlPrevious).Row

For i = 2 To lastrowD

    For j = 2 To lastrowA

        If Workbooks(alldata).Sheets("sheet1").Cells(j, 1) = Workbooks(Data).Sheets("sheet1").Cells(i, 1) _
        And Workbooks(alldata).Sheets("sheet1").Cells(j, 2) = Workbooks(Data).Sheets("sheet1").Cells(i, 2) _
        And Workbooks(alldata).Sheets("sheet1").Cells(j, 3) = Workbooks(Data).Sheets("sheet1").Cells(i, 3) _
        And Workbooks(alldata).Sheets("sheet1").Cells(j, 5) = Workbooks(Data).Sheets("sheet1").Cells(i, 5) Then

            Workbooks(alldata).Sheets("sheet1").Cells(j, 4) = Workbooks(Data).Sheets("sheet1").Cells(i, 4)

        End If

    Next j

Next i

' Fermeture de alldata

Workbooks(alldata).Close True

End Sub

Seulement, ce code est loin d'être optimisé, dans le sens où l'on parcourt toutes les lignes des fichiers à la recherche d''éventuelles modifications. Et les fichiers qui seront utilisés comporteront certainement plusieurs milliers de lignes, ce qui rendra l'opération trop longue à s'exécuter. De plus, sur cet exemple, j'ai uniquement évoqué la possibilité de changer le Status, mais au final il faudra qu'il en soit de même avec d'autres paramètres comme le prix, ou la ressource, ce qui allongera encore le temps d'exécution de ce code.

As-tu une idée d'optimisation ? Ou penses-tu que ton idée de départ soit toujours la meilleure dans cette situation ?

Merci, et bon dimanche.

C'est ce qui m'a l'air d'être le plus simple.

Par contre essaie en mettant dans une variable tes feuilles :

    Sub update()

    'Data = ActiveWorkbook.Name
SourceSht = ActiveWorkbook.Sheets("Sheet1")

    ' Ouverture de "alldata"
    Workbooks.OpenText Filename:="c:\Users\****\Desktop\alldata.xlsx", Origin:=xlWindows, _
            StartRow:=1, DataType:=xlDelimited, TextQualifier:=xlDoubleQuote, _
            ConsecutiveDelimiter:=False, Tab:=True, Semicolon:=False, Comma:=False _
            , Space:=False, Other:=False

    alldata = ActiveWorkbook.Name
MasterSht = ActiveWorkbook.Sheets("Sheet1")

    lastrowA = MasterSht.Cells.Find("*", ActiveSheet.Range("A1"), , , xlByRows, xlPrevious).Row
    lastrowD = SourceSht.Cells.Find("*", ActiveSheet.Range("A1"), , , xlByRows, xlPrevious).Row

    For i = 2 To lastrowD

        For j = 2 To lastrowA

            If MasterSht.Cells(j, 1) = SourceSht.Cells(i, 1) _
            And MasterSht.Cells(j, 2) = SourceSht.Cells(i, 2) _
            And MasterSht.Cells(j, 3) = SourceSht.Cells(i, 3) _
            And MasterSht.Cells(j, 5) = SourceSht.Cells(i, 5) Then

               MasterSht.Cells(j, 4) = SourceSht.Cells(i, 4)

            End If

        Next j

    Next i

    ' Fermeture de alldata

    Workbooks(alldata).Close True

    End Sub

Merci pour ta réponse vba-new.

Cela change-t-il quelque chose concrètement, ou est-ce juste pour avoir un code plus clair ?

De plus, est-il envisageable de passer en revue uniquement les lignes modifiées, ou doit-on absolument passer par une analyse de chaque ligne ? Je dis cela, car à priori, l'utilisateur sera souvent amené à effectuer des changements minimes (de un à trois quatre, guère plus), et malgré tout, pour mettre à jour le fichier alldate, il faudra exécuter le code sur l'ensemble des lignes déjà présentes (plusieurs milliers comme je l'ai déjà précisé... ).

Bonne soirée !

Salut m@tix,

m@tix a écrit :

Cela change-t-il quelque chose concrètement, ou est-ce juste pour avoir un code plus clair ?

Concrètement cela ne change rien du tout C'était juste une optimisation de code pas d'algo.
m@tix a écrit :

De plus, est-il envisageable de passer en revue uniquement les lignes modifiées

Maintenant que tu dis ça, pourquoi ne pas utiliser la procédure évènementielle Worksheet_Change ? Cette procédure est déclenchée lorsque tu modifies la valeur d'une cellule.

Ce que j'aurais préconisé c'est de :

1- créer un alias dans les feuilles individuelles et la feuille Globale, qui permettrait d'identifier une ligne de manière unique. Tu peux par exemple faire une concaténation de la référence, du nom de l'objet et du type

2- à chaque changement de valeur dans les feuilles individuelles, on fait une simple recherche de l'alias correspondant dans le fichier Alldate --> évite la boucle sur toutes les lignes

3- on change la valeur correspondante

4- on enregistre et on ferme le classeur Alldata

Qu'est-ce que tu en dis ?

Si ça te va, essaie de créer le code correspondant. Si tu as des difficultés, on regardera ensemble.

Bonjour vba-new,

Cela me semble en effet une très bonne idée qui optimiserait largement le temps d'exécution de la macro dans mon cas.

Cependant, je bloque dès le début pour créer des alias pour les différentes lignes en présence. Je pensais faire un algo de ce type, mais sans succès...

For i = 2 to (nbre de lignes)

        ligne &i = .cells(i,1) & .cells(i,2) & .cells(i,3)

Next i

Vois-tu d'où vient le problème ? Peut-être parce que je n'ai pas déclaré la variable ligne... ?

M@tix,

Pour que ma méthode marche il faut créer une colonne Alias dans excel !

Ex :

For i = 2 to (nbre de lignes)

         .cells(i,4) = .cells(i,1) & .cells(i,2) & .cells(i,3)

Next i

Tu fais de même dans le classeur Alldata

Ah ok !

Bon, pour la suite, je t'avoue que j'ai quelques difficultés. Dans un premier temps, j'ai cherché une façon pour détecter tout changement d'alias, et je pense que le code suivant pourrait correspondre assez bien après quelques ajustements (j'ai créé les alias sur la colonne M):

Private Sub Worksheet_Change(ByVal Target As Range)

followup = ActiveWorkbook.Name

lastrowf = Workbooks(data).Sheets("Sheet1").Cells.Find("*", ActiveSheet.Range("M1"), , , xlByRows, xlPrevious).Row

Set plage = Range("M2:M" & lastrowf) 

If Not Intersect(Target, plage) Is Nothing Then

    ligneChanged = Target.Row

End If

End Sub

Par contre, au niveau de la ligne qui définit la plage de cellules à considérer, ça ne semble pas fonctionner. En effet, j'ai 7 lignes non vides dans mon fichier, et lastrowf prend la valeur "1". Comment cela se fait-il ?

Après, je ne vois pas vraiment comment synchroniser le tout, dans lequel des deux fichiers il faut mettre le code, et comment faire la recherche des alias modifiés dans le fichier alldata.

Encore merci pour l'aide que tu m'apportes !

Re m@tix,

Ayant la flemme d'adapter ton code, voici la marche à suivre :

1- Copier le code suivant dans un module standard. Ce code te permet de créer tes alias sur la feuille active :

Sub creerAlias()
    Dim lastRow As Long, i&
    Dim temp
    lastRow = Range("a" & Rows.Count).End(xlUp).Row
    ReDim temp(1 To RowCount, 1 To 1)    'on passe par une variable tableau pour aller plus vite
    For i = 2 To lastRow
        temp(i - 1, 1) = Cells(i, 1) & Cells(i, 2) & Cells(i, 3)
    Next i
    [m:m].ClearContents
    [m1] = "alias"
    [m2].Resize(lastRow - 1).Value = temp
End Sub

2- Lancer cette procédure dans le fichier D_Data afin de créer les alias

3- Dans la procédure évènementielle Worksheet_Change de la feuille "Sheet1" du fichier D_Data, mettre le code suivant :

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim aliasATrouver As String, statut$, WbkAll$
    If Not Application.Intersect(Target, Range("d2:d" & Rows.Count)) Is Nothing And Target.Count = 1 Then
        aliasATrouver = Target.Offset(, 9)
        statut = Target.Value
        WbkAll = ThisWorkbook.Path & "\allData.xlsx"
        Workbooks.Open (WbkAll)    'ouvre le fichier allData
        creerAlias    'crée les alias dans le fichier allData
        Set c = ActiveWorkbook.Sheets("Sheet1").Range("m:m").Find(aliasATrouver, LookIn:=xlValues, lookat:=xlWhole)
        If Not c Is Nothing Then c.Offset(, -9) = statut    'si l'alias existe alors on change le statut
        ActiveWorkbook.Close True
    End If
End Sub

[u][size=150]

Important : les fichiers D_Data et allData doivent être dans le même répertoire. Sinon adapte le chemin[/size][/u]

Merci beaucoup vba-new !!

Ton code fonctionne parfaitement.

Pour commencer, j''aurais souhaité avoir quelques précisions sur les instructions suivantes :

- Je pense qu'à la ligne ReDim temp(1 To RowCount, 1 To 1) , il s'agit de lastRow à la place de RowCount non ? D'ailleurs, je ne saisis pas très bien comment cette ligne agit.

- Pour détecter un changement dans la feuille, pourquoi rajouter Target.Count = 1, à quoi cela sert-il ?

A présent, je reviens sur un point que j'avais évoqué un peu plus haut dans le fil. Vu la structure du code que tu m'as proposé, je pense que ça peut être délicat. Je souhaiterais que la mise en jour dans allData ne se fasse que via une action de l'utilisateur, une fois que ce-dernier a effectué tous les changements qu'il souhaite dans D_Data. Concrètement, pourrait-on contrôler cette action avec un bouton par exemple ? C'est vraiment important que je puisse avoir un tel contrôle. Car en fait, pour simplifier le sujet, je n'ai évoqué que le fait de pouvoir actualiser le Statut dans le fichier, mais en réalité, d'autres paramètres seront amenés à être changés par l'utilisateur, comme le prix par exemple.

Je ne sais pas si j'ai été suffisamment clair... N'hésite pas à me dire si ce n'est pas le cas !

Merci à toi.

m@tix a écrit :

- Je pense qu'à la ligne ReDim temp(1 To RowCount, 1 To 1) , il s'agit de lastRow à la place de RowCount non ?

C'est un reliquat. C'est lastRow - 1 qu'il faut mettre.

Cette ligne permet de créer une variable tableau qui contiendra tous les alias. Lorsque ce tableau est rempli, on le colle dans la colonne des alias. C'est plus rapide que de parcourir chaque ligne et mettre les alias correspondant dans chaque cellule.

m@tix a écrit :

- Pour détecter un changement dans la feuille, pourquoi rajouter Target.Count = 1, à quoi cela sert-il ?

En général, on met ce critère pour obliger l'utilisateur à ne sélectionner qu'une seule cellule et donc à n'en modifier qu'une seule.

Si plusieurs cellules sont sélectionnées, on sort de la procédure.

m@tix a écrit :

A présent, je reviens sur un point que j'avais évoqué un peu plus haut dans le fil. Vu la structure du code que tu m'as proposé, je pense que ça peut être délicat. Je souhaiterais que la mise en jour dans allData ne se fasse que via une action de l'utilisateur

C'est même plus que délicat puisque c'est contradictoire avec le code que j'ai fait ! J'avais complètement zappé ce petit détail

Tu te souviens, on s'était dit d'utiliser cette procédure évènementielle pour éviter justement de boucler sur toutes les lignes.

Boucle par laquelle on va être obligé (normalement) de passer puisqu'on ne saura pas forcément quelles cellules ont été modifiées !

Je regarde ça plus tard m@tix.

Merci pour tes précisions.

Je suis en train de creuser un peu pour voir comment il serait possible de faire en tenant compte de mes remarques précédentes, mais pour l'instant, je tourne en rond... Je ne sais pas si une solution existe. Enfin, si une idée te vient, n'hésite surtout pas !

Salut m@tix, le forum,

On va passer par une boucle, mais vu qu'on opère sur des variables tableau, ça devrait rester assez rapide.

A tester sur tes milliers de lignes.

Sub DATA_update()
    Dim WbkAll As String
    Dim RowCount As Long, RowCountAll&, lign&, lignAll&
    Dim ShtSrc As Worksheet, ShtAll As Worksheet

    Set ShtSrc = ThisWorkbook.Sheets("Sheet1")    'feuille source
    RowCount = ShtSrc.Range("a" & Rows.Count).End(xlUp).Row - 1    'nb ligne dans données source
    tabloSrc = ShtSrc.[A1].CurrentRegion.Offset(1).Resize(RowCount).Value    'met les données dans une variable tableau

    WbkAll = ThisWorkbook.Path & "\allData.xlsx"
    Workbooks.Open (WbkAll)    'ouvre le fichier allData
    Set ShtAll = ActiveWorkbook.Sheets("Sheet1")    'feuille source
    RowCountAll = ShtAll.Range("a" & Rows.Count).End(xlUp).Row - 1    'nb ligne dans données All
    tabloAll = ShtAll.[A1].CurrentRegion.Offset(1).Resize(RowCountAll).Value    'met les données du fichier allData dans une variable tableau

    'boucle de modification des statuts
    For lign = LBound(tabloSrc) To UBound(tabloSrc)
        For lignAll = LBound(tabloAll) To UBound(tabloAll)
            If tabloSrc(lign, 1) = tabloAll(lignAll, 1) And _
               tabloSrc(lign, 2) = tabloAll(lignAll, 2) And _
               tabloSrc(lign, 3) = tabloAll(lignAll, 3) Then _
               tabloAll(lignAll, 4) = tabloSrc(lign, 4): Exit For 'changement du statut et sortie de la boucle
        Next lignAll
    Next lign

    With ShtAll.[A2].Resize(RowCountAll, 6)
        .ClearContents
        .Value = tabloAll
    End With

    ActiveWorkbook.Close True
End Sub

Ici on change que les statuts. Si d'autres lignes sont susceptibles d'être changées, faudra adapter le code.

Bonsoir vba-new !

Ton code fonctionne très bien, un grand merci à toi

Donc d'après toi, le fait de passer par des tableaux entraîne une exécution plus rapide du code qu'avec celui que j'ai écrit avec de simples variables sur un grand nombre de données. Pourquoi donc ?

Si je souhaite rajouter à ton code la possibilité de mettre à jour d'autres éléments, je suppose qu'il me faudra pour chaque type d'élément considéré, rajouter une boucle similaire à celle des Status ? Ce qui, forcément, entraînera une durée d'exécution plus longue.

Bonjour m@tix,

m@tix a écrit :

Donc d'après toi, le fait de passer par des tableaux entraîne une exécution plus rapide du code qu'avec celui que j'ai écrit avec de simples variables sur un grand nombre de données. Pourquoi donc ?

Parce que le fait de parcourir ligne à ligne les données puis de les changer ligne à ligne directement dans les cellules multiplie le nombre de manipulations.

Et plus le nombre de manipulation augmente, moins c'est optimisé.

Concrètement, manipuler 10000 fois une ligne sera plus lent que manipuler en une seule fois, 10000 lignes.

Voici le principe du raisonnement par rapport au code que je t'ai donné :

1- Je mets lesdonnées dans une variable que l'on appelle variable tableau

2- Plutôt que modifier les cellules directement, je modifie les données contenues dans cette variable tableau

3- Les modifications étant faites, je colle ce tableau dans la feuille de calcul en une seule fois

m@tix a écrit :

Si je souhaite rajouter à ton code la possibilité de mettre à jour d'autres éléments, je suppose qu'il me faudra pour chaque type d'élément considéré, rajouter une boucle similaire à celle des Status ? Ce qui, forcément, entraînera une durée d'exécution plus longue.

Ça dépend. Si ces autres éléments font partie des données que l'on a mises en variable et que les conditions à vérifier sont les mêmes, tu peux rajouter tes modifs à la suite de
tabloAll(lignAll, 4) = tabloSrc(lign, 4)

Ex :

tabloAll(lignAll, 4) = tabloSrc(lign, 4)
tabloAll(lignAll, 5) = tabloSrc(lign, 5)

Bonsoir vba-new,

Je comprends à présent mieux pourquoi ton algorithme est davantage optimisé par rapport au début, merci pour tes explications, très claires !

Concernant l'ajout d'autres éléments, ce que tu as précisé est exactement ce à quoi je pensais, donc c'est parfait ! Ne me reste qu'à tout mettre en place sur mes fichiers.

Encore une fois, merci !

Bonne nuit.

Ok je te laisse faire les modifs. Si problème, reviens. Sinon si ton problème est résolu, clique sur le petit V vert stp.

Fais-en de même pour les autres posts qui ont été résolus.

En fait le fait de marquer en résolu les posts qui le sont permet aux membres qui t'aident de ne pas revenir dessus pour voir pourquoi le sujet n'a pas été résolu, alors qu'il l'est.

Rechercher des sujets similaires à "actualisation donnees fichier partir"