Forum des développeurs  

Le forum de référence en programmation et développement. Articles, cours et tutoriels du débutant au chef de projet et DBA confirmé.
Précédent   Forum des développeurs > Hardware, Systèmes et Logiciels > Microsoft Office > Access > Sondages et Débats

Sondages et Débats Forum destiné à recevoir les échanges, avis et sondages autour de la technologie Access.

Réponse
 
Outils de la discussion
Vieux 06/11/2006, 19h50   #1 (permalink)
Rédacteur/Modérateur
 
Avatar de Papy Turbo
 
Date d'inscription: mars 2004
Messages: 618
Par défaut [Cours pt-05]Moteur de mise à jour de base de données

>>>> Merci de noter toutes remarques concernant ce cours dans le sujet parallèle : [Cours papyturbo]Commentaires, remarques et suggestions
-------------------------------------------------------------------------
Ce cours est la suite
- du [Cours pt-01][Débutants]Analyse structure base de données simple et
- du [Cours pt-02][Débutants]Requête avec plusieurs sommes
- du [Cours pt-03]turbo-formulaire (les bases)
-------------------------------------------------------------------------
Le but de cet exercice est de choisir la meilleure méthode pour mettre à jour une base de données (le fichier .mdb contenant les données dans les tables), alors que nous (développeurs) avons
- développé une nouvelle application,
- modifié la structure des tables, de leurs indexes, leurs champs et des relations entre tables,
et que, pendant ce temps là, les utilisateurs ont continué à ajouter des données dans la version précédente.
Précision : Même si je suis le seul utilisateur d'une application dont je me sers tous les jours, il n'est pas question de modifier l'application avec laquelle je réalise mon travail quotidien : un bug surviendra toujours au pire moment critique, lorsqu'un client t'appelle pour avoir l'info indispensable tout de suite .
Je conseille donc de développer toujours sur des copies de l'application et des copies de la base de données, puis de faire une installation avec mise à jour de la BDD.

Nous allons utiliser les éléments suivants, chacun correspondant à un fichier ".mdb" :
- base de données (tables) à la version 1 : en cours d'utilisation. Nous allons utiliser une copie, pour les tests.
- base de données (tables) à la version 2 : nouvelle structure, à mettre en production.
- application, version 2 : à mettre en production, ici par simple copie.
- le moteur de mise à jour V1 > V2 : une mini-application Access que nous n'utiliserons qu'une seule fois.
J'ignore l'application, version 1, qui sera écrasée par la copie de l'application, version 2, pendant l'installation.
À noter, que, dans le zip ci-joint, l'application a été "splittée" en 2, en utilisant l'assistant d'Access (menu Outils > Utilitaires de base de données > Fractionner une base de données), pour séparer toutes les tables, sauf bien sûr, la table [Affaires_Selection] qui doit impérativement être une table locale, propre à chaque utilisateur (sinon, je te laisse imaginer l'embrouille ! )

Il y a 2 méthodes possibles pour réaliser cette mise à jour :
méthode #1 : reporter dans la base de données active (V1) toutes les modifications que nous avons mises au point et testées dans la base V2.
méthode #2 : créer un modèle de base en vidant les tables de la base V2, et transférer dans ces tables vides toutes les données existantes, depuis la base V1.

Quelle que soit la méthode, il va falloir interrompre les utilisateurs pendant la mise à jour : le moins longtemps possible, of course.
Une mise à jour très complexe, concernant quelques dizaines de tables, chacune ayant de 100 à quelques dizaines de milliers d'enregistrements, ne dépasse jamais quelques minutes : rarement 5 minutes, à moins que certaines opérations exigent une intervention de l'utilisateur pour faire des choix...

Je n'ai jamais utilisé la méthode #1, sauf s'il s'agit d'ajouter un seul nouveau champ dans une table, ou une nouvelle table dans la base.
Le principal argument en faveur de la méthode #2 est que je n'ai jamais été capable de faire une liste exhaustive et complète des modifications apportées à une base de données, dès que ces modifications sont un peu complexes.
Il y a toujours quelque part un index ou une propriété oubliée...

Le fonctionnement de la méthode #2 est par contre assez simple dans son principe :
- nous allons traiter chaque table dans l'ordre des niveaux d'intégrité référentielle (détails ci-dessous),
- pour les tables sans modifications majeures, une requête ajout (INSERT INTO) va transférer toutes les données de la V1 à la V2,
- nous allons contrôler les erreurs : essentiellement en comparant le nombre d'enregistrements transférés dans la V2, avec ceux de la table V1.
S'il en manque, nous ajusterons la requête pour tenir compte de chaque cas, ou nous mettrons en place des transformations plus complexes, avec une table locale intermédiaire, par exemple.
- nous recommencerons jusqu'à ce que toutes les données soient correctement transférées.
- nous passerons alors "en production" : installation de la nouvelle application + mise à jour de la base contenant les dernières données réelles.

Niveaux d'intégrité référentielle :
Cette notion, rarement mentionnée dans l'aide ou les ouvrages sur les bases de données, est primordiale pour tout transfert de données (mise à jour, synchronisation, extraits...) : nous ne pourrons pas, par exemple, copier les Affaires (niveau 1 d'intégrité) dans la base V2 si nous n'avons pas d'abord transféré tous les Clients (niveau 0).
La relation d'intégrité entre les tables Clients et Affaires de la V2 nous empêcherait d'ajouter une affaire avec une IdClients qui ne correspond à aucun client dans la table Clients.
Bien sûr, seules les règles d'intégrité de la base V2 nous concernent : celles de la base dans laquelle nous allons écrire.
Et, bien sûr toujours, il est impératif de ne jamais rien modifier dans la V1, pour pouvoir revenir en arrière, en cas de problème non prévu.

C'est pour clarifier cette notion de niveaux que je conseille toujours très vivement de disposer, dans les schémas de relations (menu Outils > Relations...),
- à gauche, les tables auxquelles n'aboutissent aucune relation, (niveau 0)
- immédiatement à leur droite, en colonne, les tables qui dépendent des premières par une relation, (niveau 1)
- continuer ainsi, de la gauche vers la droite, en alignant autant que possible les tables les unes sous les autres, par niveau.
C'est aussi pour cette raison que j'ai utilisé Excel comme outil de dessin, dans la réponse #31 du cours 01. Ça me permet de mettre clairement en évidence les numéros, comme dans le schéma ci-dessous (qui, attention, ne correspond plus à notre base V2 , mais plutôt à la V1 ) :


La toute première étape qui consiste à
- créer une base vierge pour le moteur de mise à jour (fichier SuiviAffaire_MaJ_V1_V2 2006-11-06.mdb),
- attacher toutes les tables de la V1 et celles de la V2, (Fichier > Données externes > Lier les tables...)
- différencier les tables en ajoutant le n° de version "_1" ou "_2" derrière chaque nom,
est faite, dans le zip ci-joint.

Il va donc falloir pour toi :
1- utiliser le gestionnaire de tables attachées (Outils > Utilitaires de bases de données > Gestionnaire de tables liées...) pour réattacher les tables correctement, en fonction de ton chemin d'accès,
2- me corriger (chacun son tour ) en vérifiant que les fichiers ci-joint correspondent bien aux bases de données de la version 1 (en cours d'utilisation) et 2 (celle que nous avons mise au point).
Note : tu pourras supprimer l'application (fichier SuiviAffaire_App 2006-11-06.mdb) du fichier zip. Elle ne nous intéresse plus, dans le cadre du cours 05. dans les prochains échanges, seul le moteur de mise à jour (fichier SuiviAffaire_MaJ_V1_V2 2006-11-06.mdb) devrait être modifié et zippé.
3- faire une liste complète des tables de la V2, en indiquant, pour chaque table, le niveau d'intégrité référentielle.
Fichiers attachés
Type de fichier : zip SuiviAffaires 2006-11-06.zip (146,6 Ko, 36 affichages)
Papy Turbo est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 08/11/2006, 21h36   #2 (permalink)
Nouveau membre du Club
 
Date d'inscription: mai 2006
Messages: 79
Par défaut

Citation:
Envoyé par Papy Turbo
2- me corriger (chacun son tour) en vérifiant que les fichiers ci-joint correspondent bien aux bases de données de la version 1 (en cours d'utilisation) et 2 (celle que nous avons mise au point).
J’ai repris toutes les tables de la version 1 avec correction.

Quelques commentaires sur les tables de la version 1 par rapport à la version 2.
‘Personnel’ --> ‘Employé’.
‘AvancementEtudeFournisseur’ --> ‘HeurePassee-SectionAffaire-Fournisseur’.
‘AvancementEtudePersonnel’ --> ‘HeurePasseee-SectionAffaire-Personnel’.
‘Date/Semaine’ --> ‘Date-Semaine’.
‘HeureFournisseurAffaire’ --> ‘Fournisseur-SectionAffaire’
‘HeurePersonnelAffaire’ --> ‘Personnel-SectionAffaire’.
‘HeuresAllouées’ --> HeureAllouees-SectionAffaire’.
‘SectionPerFou’ --> Section.

Joint le fichier : SuiviAffaire_Maj_V1_V2 2006-11-08.zip contenant (pour que nous travaillons sur les mêmes fichiers)
1- NI_V1.xls : Schéma du niveau d’intégrité référentiel de la version 1.
2- NI_V2.xls : Schéma du niveau d’intégrité référentiel de la version 2.
3- SuiviAffaire_BDD_V1.mdb : Base version 1.
4- SuiviAffaire_BDD_V2.mdb : Base version 2.
5- SuiviAffaire_MAJ_V1_V2 2006-11-08.mdb : début de la base de mise à jour.
-------
Fichiers attachés : SuiviAffaire_Maj_V1_V2 2006-11-08.zip (62,6 ko)
-------

Dernière modification par Papy Turbo ; 11/11/2006 à 20h09 Motif: pièce jointe
Serge57 est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 11/11/2006, 21h26   #3 (permalink)
Rédacteur/Modérateur
 
Avatar de Papy Turbo
 
Date d'inscription: mars 2004
Messages: 618
Par défaut

Tu vas t'occuper très bientôt des tables qui ont changé de nom, donc très bonne liste.
La première tâche, pour l'instant, est de créer un modèle de base de données vide et propre, au format des nouvelles tables.
Il faut donc :
- conserver une base de données version 2 avec des données dedans, au cas où on aurait encore des tests à faire sur notre application, version 2.
- utiliser une copie, que nous allons vider.

Comme je suis flemmard, je n'ai aucune envie de créer une requête pour vider chaque table. Je préfèrerais créer un assistant qui fasse le boulot aussi automatiquement que possible.
Tu trouveras
- une requête type, utilisée comme test et pour copier son code SQL dans le code VBA : *** Test suppression données
- une nouvelle table, locale, intitulée [000 DestinationTables]. Son index est basé sur le niveau d'intégrité de chaque table + le nom de la table. Je suppose que tu devines pourquoi ?
Les champs de la table devraient être évidents :
- niveau d'intégrité, J'ai suivi ton schéma NI_V2.xls pour noter les niveaux d'intégrité de chaque table.
- nom de la table,
et un nouveau
- champ DataTable, format Oui/Non, pour distinguer les tables de données des tables dites "de paramètres" ou "de données fixes".
Les tables "de données" sont celles dont nous allons récupérer les contenus à partir de la version 1.
Les tables "de paramètres" sont celles dont le contenu ne peut pas être changé par les utilisateurs.
Exemple :
À toi de décider si ton application SuiviAffaires permet ou non de créer des nouvelles sections ?
- si oui, la table Sections est une table de données, comme les autres.
- si non, tu es le seul à ajouter/supprimer/renommer ces sections et la table Sections de la version 2 doit être laissée telle quelle, avec toutes les sections dedans.
Tu trouveras également :
- un formulaire UpdateWizard, qui contient
- un onglet tabWizard, qui contient 2 pages :
... la 1ère page, Nettoyage modèle n'intéresse que nous, les développeurs, pour préparer un beau modèle de base vide.
... la 2ème page, Transfert, sera visible lorsque nous enverrons cet "assistant d'installation et mise à jour de la base de données" à notre client. Lui n'aura pas besoin de vider les tables, parce que nous lui donnerons seulement le modèle propre et vide.
Je sais que tu portes les 2 casquettes, mais il est utile pour tous les autres de bien séparer les tâches "développeur" des tâches "utilisateur/installateur".
En situation réelle, il sera facile de masquer la 1ère page avant d'envoyer l'assistant à un utilisateur.

- l'onglet Nettoyage modèle contient un sous-formulaire ssfDestinationTables basé sur la nouvelle table, et un bouton qui vide toutes les tables.
Le code de ce bouton devrait être clair ?
La seule particularité est peut être le DoCmd.SetWarnings : permet de supprimer les messages d'avertissement de Jet, lors de l'exécution d'une requête SQL. À maintenir actif pendant le débogage, pour voir et corriger les problèmes, puis à éteindre(False) lorsque le code est correct.

J'ai également ajouté, dans le module VBA, une référence à DAO, et supprimé la référence à ADO (ActiveX Data Objects), dont nous n'aurons pas besoin. En VBA, voir menu Outils > Références.

À part cela, le reste devrait être limpide :
- on travaillera toujours à partir des tables de destination, celles dont les règles (d'intégrité, d'indexation avec/sans doublon, de champs nulls admis/refusés, etc.) devront être respectées,
- on ne peut vider ces tables qu'en commençant par le dernier niveau d'intégrité. Pause : la raison est évidente ?
- il reste un mini-bug , à résoudre avant de supprimer les Stop et autres commentaires destinés au débogage.
Le mini-bug est juste là pour mette en évidence la méthode générale :
On avance pas à pas, en essayant bêtement de "faire le travail" (vider toutes les tables de données, puis copier leurs contenus...).
À chaque étape, on lance le processus complet, et on corrige les erreurs au fur et à mesure qu'elles apparaissent.
Cette méthode plante nécessairement au début, mais elle va beaucoup plus vite que d'essayer de tout prévoir d'avance. Ça sera encore + évident + tard, quand ça va se compliquer...

Bref, le travail pour toi va consister à
- réparer le dernier bug qui plante,
- préparer la prochaine étape : juste un bouton Mise à jour dans le 2ème onglet, qui va transférer toutes les données depuis chaque table de la version 1 dans chaque table de la version 2.
Tu peux reprendre le modèle de code que j'ai utilisé pour vider les tables, évidemment en inversant l'ordre des tables (c'est bien clair, ça ? )
En remplaçant le code de la requête SQL de Suppression par une requête Ajout, qui copie toutes les données d'un table V1 dans la même table V2.
Tu vas avoir des problèmes, ne serait-ce que dès qu'une table aura changé de nom : pitête qu'un nouveau champ dans la table 000 DestinationTables, genre un champ SourceTableName, pourrait résoudre ce 1er problème ?

N'essaye pas de résoudre tous les problèmes d'un coup : pour l'instant, juste un transfert standard, très générique, qui ne marchera correctement que pour les tables dont les champs n'ont pas changé.
Rappel : ici, nous avons volontairement fait de profondes modifications à la structure de la base. En temps "normal", seuls un nouveau champ par ci-par là, ou quelques nouvelles tables... nécessiteront un traitement spécial. Mais la plupart des tables ne changent pas d'une version à l'autre.
Commençons donc avec cette routine générique qui fera le gros des transferts.
Bon courage.
Et si tu es bloqué, suffit de crier très fort.

P.S. : je suis gentil : je ne t'ai pas demandé de faire un Form_Resize du formulaire, de l'onglet, du sous-formulaire... bien que tu saches maintenant faire ça en 2 minutes.
Parce que c'est juste un outil qui va tourner une seule fois.
Mais si tu veux faire l'exercice, faut pas te gêner.
Fichiers attachés
Type de fichier : zip SuiviAffaire_MaJ_V1_V2 2006-11-11.zip (17,6 Ko, 18 affichages)
Papy Turbo est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 14/11/2006, 16h23   #4 (permalink)
Nouveau membre du Club
 
Date d'inscription: mai 2006
Messages: 79
Par défaut

Une initiative : Renommer toutes les tables en mettant le numéro de la table en tête du nom de la table (plus facile et moins de risque lors de la sélection pour ‘gestion des tables liées’). Donc reprise du code ‘Vider les tables de données’.


Citation:
Envoyé par Papy Turbo
À toi de décider si ton application SuiviAffaires permet ou non de créer des nouvelles sections ?
Normalement elle permet, mais pour l’exemple on peut dire que NON.

Citation:
... la 1ère page, Nettoyage modèle …
Etape fonctionnelle.

Citation:
... la 2ème page, Transfert,
Création d’un champ supplémentaire dans la table 000_destinationTables (SourceTableName) pour y rajouter les noms des tables Sources.

Création d’une requête pour nouveau sous formulaire ‘FUpdateWizard_002_TransfertData’ pour la lecture de la table source et table destination.

Création d’un sous formulaire ‘UpdateWizard_002_TransfertData’ et insertion dans le 2ème onglet du formulaire ‘UpdateWizard’.

Dans le 2ème onglet rajout d’un bouton de commande ‘cmdMAJTables ‘ Mise à Jour Tables.

Puis je suis bloqué ( cause des différents nom de champ dans les tables).
J’ai envisagé une solution : création d’une table du même style que ‘000 DestinationTables’ mais cette fois avec la liste de tous les champs ??

Donc avant de continuer je crie très fort !!!

-------
Fichiers attachés : SuiviAffaire_MaJ_V1_V2 2006-11-14.zip (29,3 ko)
-------

Dernière modification par Papy Turbo ; 16/11/2006 à 20h42
Serge57 est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 16/11/2006, 20h32   #5 (permalink)
Rédacteur/Modérateur
 
Avatar de Papy Turbo
 
Date d'inscription: mars 2004
Messages: 618
Par défaut

Citation:
Envoyé par Serge57
Une initiative
1ère bonne surprise !
Ça veut dire que tu commences à être à l'aise, donc continue comme ça.
Il vaut mieux plein d'initiatives, quitte à te planter : il n'y a que comme ça qu'on apprend !

Sur ce point, l'inconvénient de mettre le numéro de version avant, c'est que j'aime bien avoir la table version 1 + la même, version 2, + selon les besoins une 3ème table, locale, temporaire, qui porte le même nom.
Tu verras donc toi même, au bout de quelques essais, ce que tu préfères : version par devant ou par derrière (sur l'air de ...)

2ème bonne surprise : le redimensionnement des formulaires marche impec ! C'est top, et ça va nous permettre d'ajouter encore des champs à notre "table des tables".

Diverses retouches :
J'ai réactivé l'Echo False, au début de Form_Resize : c'est nettement plus agréable,sans le scintillement.

Par contre, je ne sais pas si on va avoir besoin de 2 copies du sous-formulaire et de la table des tables ?
Une seule devrait suffire, pourvu qu'on ait tous les champs, et, a priori, inutile d'afficher ça aux utilisateurs de l'installation.
En tout cas, il faudra qu'ils ne puissent rien modifier.
On reverra ça plus tard. Pour l'instant, on peut s'en servir pour montrer l'avancement, table par table.

Dans le code VBA du formulaire, tri des routines : j'ai utilisé la commande de mzTools : Autres utilitaires > Trier les procédures.
De manière à avoir, en tête, tous les évènements du formulaire, suivis, dans l'ordre, de nos 2 boutons. (hé oui, je suis maniaque !)

Contrôle d'erreur : comme dans Form_Resize, qui doit impérativement faire un Echo True avant de sortir, nous devrons impérativement, au delà de la phase de débogage, faire un SetWarnings True.
J'ai donc recopié le code de contrôle d'erreur de Form_Resize, pour être sûr de toujours exécuter cette commande, même si une erreur imprévue se produit.

Formulaire principal (conteneur) : supprimé le sélecteur d'enregistrement (y a pas d'enregistrements), et les boutons de navigation.

cmdMAJTables_Click : planté
Quand on supprime des enregistrements, on commence par le dernier niveau d'intégrité, et on remonte vers le premier.
Quand on ajoute, ou qu'on modifie des enregistrements, on commence par le niveau le plus bas (0), et on remonte.
Il faut que cette notion devienne évidente.
C'est la conséquence logique de l'intégrité : pas d'Affaire qui ne corresponde à aucun Client... donc
- suppression des Affaires d'abord, puis suppression des Clients,
- ajout des nouveaux Clients en premier, suivi de l'ajout des Affaires.

En ce qui concerne notre transfert de données, j'ai rien fait !
Je suis trop flemmard (je me répète, mais je pense que tout bon développeur est un flemmard qui préfère faire travailler son ordi, plutôt que de faire le travail "a la main") pour
1- reconnecter les tables une par une, même avec le gestionnaire de tables attachées,
2- noter tout ce qu'il se passe, et surtout les messages d'erreur.

Tu trouveras donc un ensemble de classes objet (j'espère que ça te fait peur ) et de modules, dont certaines routines importées directement de la FAQ Access (à consommer sans modération) :
Dans l'ordre où ils s'exécutent :
- une macro Autoexec qui lance une routine d'initialisation : App_Init(). Cette routine initialise les objets dont nous avons besoin.
- un module _Demarrage_Utilitaires qui, comme son nom l'indique, contient les routines de démarrage (App_Init(), et de fin : App_Quit() qui ferme proprement les mêmes objets) + divers utilitaires dont nous aurons besoin,
- une classe clApplication, qui va servir à créer un objet ThisApp, auquel on va donner toutes les propriétés et les méthodes qu'on ne trouve pas dans l'objet Application d'Access : un nom, un titre, même un journal, etc. selon les besoins...
- une classe clLog (Log = journal) qui va servir à créer le journal dont dispose notre nouvelle application (objet ThisApp.Log).

Les codes extraits de la Faq sont déjà commentés dans la Faq.
Y a t-il besoin de commentaires sur les différences entre un module de classe et un module ordinaire ?
Disons seulement que je me suis permis d'ajouter des propriétés (Name, Title...) directement en tant que variables Publiques, en tête du module de classe, alors qu'il existe une méthode + lourde, avec Property Get et Property Let...
Mieux vaut voir les divers cours d'initiation aux classes, dans la rubrique Cours de Développez, entre autres :
- Création d'une classe de manipulation de chaînes de caractères
- Tout comprendre à propos des modules de classes sous Access
- La notion de la classe formulaire Access par l'exemple
etc.
Je passe sous silence la classe "Images" de Thierry Gasperment, qui est nettement plus tordue...)


Il y a déjà une routine pour reconnecter les tables dans la FAQ : Rétablir les liaisons des tables liées après déplacement d'une base fractionnée par Tofalu.
Celle qui figure dans la classe clApplication, la méthode CheckTablesConnection() fait un poil plus de travail pour automatiser cela, dans le cadre d'une application Access standard (toutes mes applications démarrent par une routine beaucoup plus complexe que celle-là, mais qui fait à peu près la même chose) :
- elle parcourt les tables,
- elle ignore les tables locales,
- elle vérifie si les connexions sont bonnes en essayant d'ouvrir chaque table attachée,
Dès qu'elle en trouve une dont la connexion est rompue,
- elle extrait le nom de la base concernée (nous sommes ici connectés à 2 bases distinctes)
- elle ouvre une boîte de dialogue pour demander où se trouve la bonne base à rattacher,
- et elle reconnecte toutes les tables qui étaient liées à la même base (ça, c'est le seul point qui m'intéresse en temps que flemmard professionnel )

Tout ça pour quoi ?
En dehors de la reconnexion des tables, tu constateras, dans la routine cmdEmptyDataTables_Click() quelques retouches : il y a notamment un journal qui se crée dans le même dossier que l'application, pour noter, soit les erreurs, s'il y en a, soit le bon déroulement de notre mise à jour.
Je te laisse donc, pour l'instant,
1- digérer les nouveautés (bon appétit),
2- appliquer la même méthode à ta routine cmdMAJTables_Click()
Le but à atteindre :
- que tout s'exécute, même s'il y a des erreurs,
- qu'on puisse ensuite ouvrir le journal et voir quelles sont les tables dont les données ne sont pas passées.
Pour cela, il serait bien de
1- avoir un On Error Goto qui enregistre les erreurs (Number, Description) dans le journal, avec le nom de la table concernée, puis qui continue quand même (Resume Next),
2- avec ou sans erreur, dans le journal, après exécution de la requête SQL Ajout, de
- comparer le nombre d'enregistrements de la table V1 avec celui de la table V2,
- s'ils sont égaux, OK - TVTB
- s'ils sont différents : *** erreur !!!

On verra alors, une par une, comment les traiter (requête spécifique, code...)
Dans un cas réel, comme je l'ai déjà dit, seules quelques tables seront modifiées et "ne passeront pas" : la méthode avec journal permet de les identifier, sans se préoccuper des autres. C'est le but de cet exercice : te faire gagner du temps à chaque fois que tu recréeras un moteur de mise à jour à partir de ce modèle, pour les prochaines versions de SuiviAffaires, ou autres applis.
Fichiers attachés
Type de fichier : zip SuiviAffaire_MaJ_V1_V2 2006-11-16.zip (63,6 Ko, 12 affichages)
Papy Turbo est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 20/11/2006, 16h30   #6 (permalink)
Nouveau membre du Club
 
Date d'inscription: mai 2006
Messages: 79
Par défaut

Citation:
Envoyé par Papy Turbo
Quand on supprime des enregistrements, on commence par le dernier niveau d'intégrité, et on remonte vers le premier.
Quand on ajoute, ou qu'on modifie des enregistrements, on commence par le niveau le plus bas (0), et on remonte.
Il faut que cette notion devienne évidente.
J’avais compris, mais n’étant pas un virtuose de VBA, (j’ai pas vu ‘.MoveLast’ dans le code), j’avais fait un tri décroissant dans la requête ‘FUpdateWizard_002_TransfertData’ et ça avait l’air de marcher !!!!


Citation:
1- digérer les nouveautés (bon appétit),
mal à l'estomac ! (mais enfin j'ai signé pour ça )


Citation:
2- appliquer la même méthode à ta routine cmdMAJTables_Click()

1- avoir un On Error Goto qui enregistre les erreurs (Number, Description) dans le journal, avec le nom de la table concernée, puis qui continue quand même (Resume Next),
J’ai appliqué la même méthode .

Citation:
2- avec ou sans erreur, dans le journal, après exécution de la requête SQL Ajout, de
- comparer le nombre d'enregistrements de la table V1 avec celui de la table V2,
- s'ils sont égaux, OK - TVTB
- s'ils sont différents : *** erreur !!!
Pour l’instant aucune table ne passe sont erreur (problème de nom de champ, de table inexistante, ….)

Ci joint Application avec un embryon de code pour la commande cmdMAJTables_Click ?? pour gestion dans le journal et correction. (c'est une idée de code .
-------
Fichiers attachés : SuiviAffaire_MaJ_V1_V2 2006-11-17.zip (66,0 ko)
-------

Dernière modification par Papy Turbo ; 23/11/2006 à 12h07
Serge57 est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 23/11/2006, 13h03   #7 (permalink)
Rédacteur/Modérateur
 
Avatar de Papy Turbo
 
Date d'inscription: mars 2004
Messages: 618
Par défaut Encore un poil de POO

Modifs de notre moteur, dans le désordre :
- Table 000 DestinationTables : changé le champ IntegrityLevel, de texte en numérique - byte (j'aimerais bien voir une base Access avec + de 255 niveaux d'intégrité !)
- j'ai bien aimé ta variable PasErreur (sauf qu'elle aurait dû être Booléenne, pas Integer !). Mais je l'ai virée de manière à inclure systématiquement le nombre d'enregistrements source/destination : même s'il y a erreur, je veux savoir si quelque chose passe, et combien. Ce qui pourra révéler un problème d'index unique..., par exemple.
Et puis, j'ai déjà la réputation d'être trop bavard dans mes commentaires, alors, j'aime bien que mes logs aient les mêmes défauts
- supprimé la moitié des erreurs (message plus clair dans le nouveau journal, ça aide), en remplaçant
Code :
SourceCountRecords = DCount("*", VERSION1 & !TableName)
par
Code :
SourceCountRecords = DCount("*", VERSION1 & !SourceTableName)
Un peu de POO, histoire de rigoler les jours de pluie.
Non, c'est pas pour dire Poo Poo comme un fantôme holywoodien, ni que j'ai fait un gros Poopoo, c'est juste Programmation Orientée Objet.
J'insiste lourdement : la POO n'est pas un luxe réservé aux "pros". Si tu sais faire des Sub et des Functions dans un module standard, tu sais faire aussi les mêmes dans un module de classe. Quand aux Propety Get / Let / Set (pour les objets), c'est pas sorcier.
Il y a même, avec mzTools, un assistant qui transforme une variable publique en 2 propriétés Get/Let... (Autres utilitaires > Convertir la variable publique en propriétés...)
Pour de bonnes bases, voir les cours, déjà cités en réponse #5.
- J'ai donc complété la classe clLog :
... l'évènement Class_Initialize() se déclenche à la création de n'importe quelle classe (déjà vu dans la clase clApplication) : on en profite pour noter une fois pour toutes le chemin d'accès dans une variable locale.
... propriétés Path et Name : permettent au programmeur, dans son code, de changer le chemin et/ou le nom du journal, selon ses besoins.
Remarque 1 : quand on traite des fichiers avec un chemin (path) + un nom (Name ou Filename), je conselle de
- toujours séparer clairement le chemin du nom, ce qui permet de changer le nom facilement, indépendamment du chemin.
- systématiquement terminer le chemin par un "\", ce qui permet de trouver le fichier à "Path & Name", en évitant l'erreur si il manque le dernier "\".

Remarque 2 (POO) : Noter la différence entre ces 2 propriétés du journal (classe clLog) et les propriétés Public ... As ... de la classe clApplication.
La méthode utilisant les 2 routines Get + Let est la méthode standard.
Le raccourci qui utilise une simple variable publique est propre à VBA, et ne se retouve généralement pas dans d'autres langages. On retrouvera donc ce raccourci dans tous les langages basés sur VBA : Excel VBA, Visual Basic (VB6), etc.
Gros avantage de la méthode POO standard , avec Get + Let (+Set pour les propriétés objet), les propriétés des objets deviennent plus "intelligentes" : ici, par exemple, on en profite pour vérifier la syntaxe du chemin (qui doit se terminer par un "\") et l'extension du nom de fichier du journal (".log")
... du coup, pour simplifier, n'ayant plus besoin de la propriété Name de l'application, j'ai supprimé cette propriété qui n'était utilisée que pour créer le nom du journal. Remplacée, dans AppInit(), par ThisApp.Log.Name = "..."
Enfin, pour en finir avec la POO du jour, note que j'aurais pu créer une routine Public Sub DeleteLog(), et une autre DisplayLog(), dans notre module Utilitaires. Allez, j'avoue : j'avais commencé comme ça, avec DisplayLog(). Puis je me suis tapé sur les doigts
Mais c'est quand même nettement plus élégant, et plus clair pour un autre programmeur qui découvre ton application, de mettre ces 2 routines directement dans l'objet Log, non ?
---------------------
Retour à nos moutons.
Solution, pour transférer la table Clients :
- création d'une requête spécifique.
Astuce, pour les débutants, s'il y en a : dans la fenêtre de création de requêtes d'access, pour insérer d'un coup tous les champs d'une table dans une requête :
+ double clic sur l'en tête de la table, pour sélectionner tous les champs
+ faire glisser le paquet vers les colonnes
Dès qu'on choisit la table de destination, Access met en face tous les noms de champs qui correspondent, il n'y a plus qu'à compléter les noms qui ne correspondent pas (liste déroulante).
Ensuite, pour que le "moteur" exécute cette requête,
- ajout d'un champ dans la table [000 DestinationTables]
- ajout des nouveaux champs dans les requêtes [FUpdateWizard_001_DestinationTables] et [FUpdateWizard_002_TransfertData]
Noter que les 2 sont identiques : on simplifiera cela + tard.
- ajout du nouveau champ dans le sous-formulaire [UpdateWizard_002_TransfertData]
- code de cmdMAJTables_Click : ajout d'un test - Si une requête est spécifiée, on la lance à la place de notre requête standard.
Si tu lances maintenant le transfert, il y a une table (sur 12) qui passe bien. Elle est pas belle, la vie ?
Autrement dit, dans ton cas, il n'y a plus qu'à créer des requêtes spécifiques pour chaque table, et découvrir d'autres problèmes à résoudre...
Ce qui veut dire que tout ce qu'on a fait jusque là ne sert à rien ? Ca sera pour ta prochaine mise à jour, peut être plus simple que celle la ?
Fichiers attachés
Type de fichier : zip SuiviAffaire_MaJ_V1_V2 2006-11-23.zip (75,3 Ko, 8 affichages)
Papy Turbo est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 26/11/2006, 15h28   #8 (permalink)
Nouveau membre du Club
 
Date d'inscription: mai 2006
Messages: 79
Par défaut

Citation:
Envoyé par Papy Turbo
Autrement dit, dans ton cas, il n'y a plus qu'à créer des requêtes spécifiques pour chaque table, et découvrir d'autres problèmes à résoudre...
Aucun souci pour les tables de 1er niveau, mais pour le 2éme niveau ça se complique !!

L’intégrité référentielle, la référence de certaine liste de choix, (clients, Employes) bloquent l’ajout d’enregistrement.

Donc
Citation:
et découvrir d'autres problèmes à résoudre...
voilà les nouveaux problèmes .

Piste pour la référence des listes de choix,
Donc je ne vois que 3 possibilités pour y arriver.
1 – Renommer la table « 2-Clients » en « Clients »
2 - Modifier la zone de liste modifiable pour quelle pointe sur la table « 2-Clients ».
3 – Créer une copie de la table « 2-Clients » en « Clients » (en table locale).

Par contre pour l’intégrité référentielle, je n’ai aucune piste. (sauf détruire la relation et la reconstruire ensuite. Mais comment !!!)

Donc joint table avec mise à jour des tables 1er niveau, et .... .

-------
Fichiers attachés : SuiviAffaire_MaJ_V1_V2 2006-11-24.zip (79,9 ko)
-------

Dernière modification par Papy Turbo ; 30/11/2006 à 11h13 Motif: pièce jointe
Serge57 est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 30/11/2006, 12h41   #9 (permalink)
Rédacteur/Modérateur
 
Avatar de Papy Turbo
 
Date d'inscription: mars 2004
Messages: 618
Par défaut

Youpi, c'est la vraie galère qui commence !

Avant de mettre le doigt sur une solution, précisons clairement ce que nous ne voulons surtout pas faire :
Citation:
détruire la relation et la reconstruire ensuite
Cette proposition est parfaitement naturelle : tous les "responsables base de données" que je connais, y compris quelques très bons maestros du Client/Serveur sous SQL Server, Oracle et divers ERP... en sont passés par là, et bon nombre le font encore (hou, la honte ! Non, je rigole. C'est la solution normale de facilité, à court terme.)

Ce que nous voulons : un moteur qui transfère le tout, sans rien perdre : ni aucune donnée, bien sûr, ni aucune "meta-donnée", à savoir l'ensemble des règles et propriétés des tables, des champs, des relations qui décrivent la structure de la base.
Une fois au point, notre moteur sera xxx fois plus rapide qu'un spécialiste qui
- enlève les règles d'intégrité,
- nettoie toute les tables "à la main" (doublons, champs nuls/non nuls, strings vides non autorisés, etc.)
- remet chaque règle en place, et
- recommence le nettoyage x fois, jusqu'à ce que toutes les nouvelles règles "passent".

Pour trouver ce qui coince dans le transfert, on peut :
- examiner les propriétés de chaque champ et surtout, comparer chaque index, en particulier les indexes uniques, en
::: ouvrant les tables V1 et V2 en mode création, côte à côte,
::: fenêtre des indexes ouverte et
::: clic sur celle de gauche -> clic sur celle de droite...

Si rien n'est évident :
- copier un des enregistrements "qui coince" (en l'occurence, n'importe lequel ), à la main, champ par champ, jusqu'à trouver le champ "qui ne passe pas".
Attention de ne pas recopier ce qu'on voit dans la table source, mais bien de faire un copier/coller du champ source dans le champ destination.
Sans cette précaution, on aurait pu rater le problème posé par le champ [codepersonnelRP] (légende : "Personnel PROJET") :


Le piège était particulièrement tordu :
- table [1_Affaire], champ [codepersonnelRP] : la valeur par défaut est 0 (zéro),
- on n'a rien mis comme donnée, donc tous les RP sont à zéro !
- il n'y a aucun employé avec la clé 0,
- dans la base V1, pas d'intégrité : ça passe.
- dans la table V2, intégrité : ça coince.
C'est bien ce qu'on veut : empêcher ce genre d'incohérence.

Le pire, est que, quand Access essaye d'afficher le nom de l'employé, spécifié par la liste déroulante du champ [codepersonnelRP], il ne trouve aucun employé.
Dans ce cas, généralement, il n'affiche même pas la valeur '0' de la clé.
Si on ouvre la base version 1, il n'affiche rien, et on croit à tord, que le champ est vide, c'est à dire : valeur nulle !
Heureusement pour nous, lorsque la table est attachée et renommée ("1_Affaire"), la liste déroulante ne marche plus et, comme ci-dessus, on voit la valeur "0".

Il va donc falloir :
- refaire entièrement ta requête V1_V2_n1_01_Affaires, en y incluant bien tous les champs de chaque table,
- pour le champ [codepersonnelRP], faire un petit calcul : si valeur = 0, faut coller la valeur 'Null'.

Je te laisse faire le test + mise au point et, dans la foulée, profiter de notre table Affaires pour tester un problème très courant (peut être le plus courant de tous ?) dans la vie réelle : résoudre les doublons.

L'exercice consiste, après avoir résolu le problème ci-dessus et correctement transféré nos 4 affaires,
- ouvrir la table [1_Affaire],
- comme dans l'image ci-dessus, changer le titre de la dernière affaire, de "Affaire 4" en "Affaire 2",
- relancer tout le transfert en effaçant le contenu des tables...
De manière à avoir un doublon sur le titre : c'est permis en V1 (champ mémo, donc non indexé). Pas acceptable en V2 : champ avec index unique.
Donc, pendant la prochaine exécution, tu dois voir le message d'erreur suivant, pendant le transfert de la table Affaires :
Citation:
[...] 1 enregistrements n'ont pas été ajoutés à la table à la suite de violations de clé, [...]
et, dans le journal, il doit y avoir :
Code :
=========================== niveau 1 ===========================
    Transfert de la table [Affaire] vers la table [Affaires]
*** Erreur de transfert
    La table source [1_Affaire] contient 4 enregistrements.
    La table destination [2_Affaires] contient 3 enregistrements.
--------------
Puis réfléchir au moyen le plus simple de récupérer quand même ce titre en doublon ????
Papy Turbo est déconnecté   Envoyer un message privé Réponse avec citation
Vieux 01/12/2006, 21h31   #10 (permalink)
Nouveau membre du Club
 
Date d'inscription: mai 2006
Messages: 79
Par défaut

Citation:
- pour le champ [codepersonnelRP], faire un petit calcul : si valeur = 0, faut coller la valeur 'Null'.
Ok. Et en plus ça fonctionne.

Citation:
profiter de notre table Affaires pour tester un problème très courant (peut être le plus courant de tous ?) dans la vie réelle : résoudre les doublons……
Puis réfléchir au moyen le plus simple de récupérer quand même ce titre en doublon ????
Le moyen le plus simple est de modifier les titres pour qu'il ne soient pas identique mais cohérent avec ce qui existe. Donc rajout au début du titre existant .... le champ "codeaffaire" qui lui est toujours pas définition différent.

Citation:
- comme dans l'image ci-dessus, changer le titre de la dernière affaire, de "Affaire 4" en "Affaire 2",
Pour supprimer le doublon:
1- Création d’une requête pour trouver les doublons. Requête 01_1_DoubonTitreAffaire.J’ai modifié manuellement le type de donnée du champ Titre Affaire (mémo => texte de 255 caractères) de la table affaire, base V1 pour que la requête fonctionne.
2- Création de la requête 01_2_ModifDoublon pour mis à jour du titre de l’affaire. Concaténation du champ 'codeaffaire' et du 'Titre Affaire' existant. C’est une Idée !!!

Ensuite, je me suis lancé sur la mise en forme du niveau 2. ET…. M…. . la table « SectionsAffaires » n’existe pas en version 1. Donc il faut la créer !!(la palice).J’ai créé une requête ajout V1_V2_n2_01_SectionsAffairesMais problème (ou pas), la combinaison des 2 tables (sections et affaires) crée des d’enregistrements pas toujours utiles pour la suite….. Mais je n'ai pas trouvé d’autre solution. (par contre j'ai modifié un peu le code 'cmdMAJTables'. problème pour compter des enregistrements sur une table qui n'existe pas')


Ensuite, je me suis lancé sur la mise en forme du niveau 3, ET… MMM….
1- Dans la base V1, manque une relation d’intégrité entre table ‘affaire’ et table ‘ HeurePersonnelAffaire’
2- pas d’idée pour créer un automatisme de transfert !!!.
-------
Fichiers attachés : SuiviAffaire_MaJ_V1_V2 2006-11-30.zip (80,8 ko)
-------

Dernière modification par Papy Turbo ; 11/12/2006 à 16h17 Motif: pièce jointe
Serge57 est déconnecté   Envoyer un message privé Réponse avec citation