par , 22/03/2020 à 21h30 (1647 Affichages)
On présente dans ce billet une alternative aux champs multivalués utilisés dans les tables Access.
I. Introduction
Les champs multivalués permettent d'afficher directement dans les tables, les requêtes ou les formulaires, des listes de choix avec des cases à cocher pour sélectionner des données provenant d'une autre source.
Par exemple, on peut choisir les employés devant participer à une visioconférence directement dans la table des événements :

Cependant, comme ces champs peuvent contenir plusieurs valeurs pour un même enregistrement, ils ne répondent pas à la première forme normale de la théorie de la normalisation, nécessaire pour concevoir un bon schéma d'une de base de données.
Leur utilisation dans les requêtes comme dans le code peut ainsi sembler déroutante aux puristes.
En résumé, ils peuvent par la suite compliquer le développement et la maintenance de la base Access.
Pour éviter ces problèmes, on se basera pour réaliser notre exemple, sur le modèle conceptuel de données suivant :

On a une relation plusieurs-à-plusieurs dans laquelle un employé peut participer à un ou plusieurs événements, de même un événement peut regrouper un ou plusieurs employés.
On va donc devoir créer une table intermédiaire entre les tables T_Employe et T_Evenement nécessaire pour concevoir un bon schéma relationnel.
II. Description du contexte
On souhaite réaliser une liste permettant de choisir les employés devant participer à un événement (visioconférence, réunion, etc..).
Pour cela, on va utiliser un sous-formulaire basé sur une requête affichant la liste des employés accompagnés de cases à cocher pour effectuer les choix :

On bénéficiera ainsi en plus, de tous les avantages qu'offrent les sous-formulaires par rapport aux zones de liste, notamment au niveau de la mise en forme.
III. Tables nécessaires
III-A. T_Employe
Elle permet d'enregistrer les données concernant les employés.
| Nom du champ |
Type de données |
Description |
| IdEmploye |
NuméroAuto |
Identifiant de l'employé |
| NomEmploye |
Texte |
Nom de l'employé |
| PrenomEmploye |
Texte |
Prénom de l'employé |
| DateEntree |
Date/Heure |
Date d'entrée de l'employé dans la société |
| DateSortie |
Date/Heure |
Date de sortie de l'employé de l'entreprise |
III-B. T_Evenement
Elle contient les informations relatives aux événements.
| Nom du champ |
Type de données |
Description |
| IdEvent |
NuméroAuto |
Identifiant de l''événement |
| ObjetEvent |
Texte |
Objet de l'événement |
| EmplacementEvent |
Texte |
Lieu de l'événement |
| DateEvent |
Date/heure |
Date de l'événement |
| HeureDebutEvent |
Date/heure |
Heure de début de l'événement |
| HeureFinEvent |
Date/heure |
Heure de fin de l'événement |
III-C. T_Employe_Evenement
Elle permet de faire le lien entre les 2 tables précédentes.
| Nom du champ |
Type de données |
Description |
| IdEmploye |
Entier long |
Identifiant de l'employé : clé étrangère |
| IdEvenement |
Entier long |
Identifiant de l'événement : clé étrangère |
La clé primaire est composée des champs IdEmploye et IdEvenement, elle permet ainsi d'identfier de manière unique l'enregistrement de la table, on est donc assuré de ne pas avoir deux fois le même employé pour un événement donné.
III-D. T_Employe_Evenement_Temp
Elle permet de faire le lien entre les 2 tables précédentes.
| Nom du champ |
Type de données |
Description |
| IdEmploye |
Entier long |
Identifiant de l'employé : clé étrangère |
| IdEvenement |
Entier long |
Identifiant de l'événement : clé étrangère |
| Participant |
oui/non |
Indique si l'employé participe ou pas à l'événement. |
La colonne Participant a comme valeur par défaut Non.
La clé primaire est placée sur le champ IdEmploye. Cette table est mise à jour à chaque changement d'enregistrement sur le formulaire.
IV. Relations entre les tables
On définit :
- Une relation 1 à plusieurs entre les tables T_Employe et T_Employe_Evenement sur le champ IdEmploye.
- Une relation 1 à plusieurs entre les tables T_Evenement et T_Employe_Evenement sur respectivement les champs IdEvent et IdEvenement.

V. Requêtes
V-A. R_Employes_Evenement
Elle relie la table temporaire avec celle des employés, et affiche, pour chaque événement, la liste des employés accompagnés de leur participation et ordonnés suivant leur nom et prénom :

Elle constitue la source de données du sous-formulaire destiné à afficher la liste de choix des employés filtrés en fonction de leurs dates d'entrée et de sortie dans l'entreprise.
V-B. R_Ajouter_Employes
Cette requête insertion ajoute les nouveaux employés dans la table temporaire :
1 2 3 4
| INSERT INTO T_Employe_Evenement_Temp ( IdEmploye )
SELECT T_Employe.IdEmploye
FROM T_Employe LEFT JOIN T_Employe_Evenement_Temp ON T_Employe.IdEmploye = T_Employe_Evenement_Temp.IdEmploye
WHERE IsNull([T_Employe_Evenement_Temp].[IdEmploye])=True; |
Elle est exécutée à l'ouverture du formulaire pour mettre à jour la table temporaire.
VI. Formulaire principal
Il est basé sur la table T_Evenement et affiche les informations sur un événement. Il contient en plus le sous-formulaire permettant de choisir les employés participants à l'événement.

VI-A. Procédure sur ouverture du formulaire
Le code insére les nouveaux employés dans la table temporaire :
1 2 3 4 5 6 7 8 9
| Private Sub Form_Open(Cancel As Integer)
Dim db As DAO.Database ' variable objet pour faire référence à la base de données
Set db = CurrentDb ' référence à la base courante
db.Execute "R_Ajouter_Employes", dbFailOnError ' on ajoute les nouveaux employés dans la table temporaire
Set db = Nothing ' on libère la variable objet
End Sub |
On exécute dans ce code la requête insertion R_Ajouter_Employes vu précédemment.
VI-B. Procédure sur activation
Ce code met à jour la liste des employés dans le sous-formulaire, sur activation d'un enregistrement, c'est à dire quand on se positionne sur un enregistrement.
Déroulé de la procédure événementielle :
- Si on est sur un nouvel enregistrement, on génrère un nouveau NuméroAuto dans le champ IdEvent de la table T_Evenement.
- On met à jour dans la table T_Employe_Evenement_Temp, l'identifiant de l'événement et la participation de l'employé.
- On rafraîchit le sous-formulaire basée sur la requête R_Employes_Evenement, pour afficher la liste des employés accompagnés de cases à cocher.
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
| Private Sub Form_Current()
Dim db As DAO.Database ' variable objet pour faire référence à la base courante
Dim sSQL As String ' variable contenant la chaîne SQL
If IsNull(Me.IdEvent) Then ' si on est en mode ajout, sur un nouvel enregistrement
Me.DateEvent = Date() ' on saisit une valeur dans un champ, ce qui génère le numéro-auto dans le champ IdEvent
End If
Me.cmbRechercher.Value = Me.IdEvent ' on copie l'identifiant de l'événement dans la liste de recherche
Set db = CurrentDb ' référence à la base courante
' Constitution de la chaîne SQL destinée à mettre à jour la table temporaire avec l'identifiant de l'événement
sSQL = "UPDATE T_Employe_Evenement_Temp " & _
"SET T_Employe_Evenement_Temp.IdEvenement = " & Me.IdEvent & ", T_Employe_Evenement_Temp.Participant = False;"
db.Execute sSQL, dbFailOnError ' on déselectionne tous les employés dans la table temporaire
' Constitution de la chaîne SQL destinée à mettre à jour la table avec les données saisies dans la table T_Employe_Evenement
sSQL = "UPDATE T_Employe_Evenement_Temp INNER JOIN T_Employe_Evenement ON (T_Employe_Evenement_Temp.IdEmploye = T_Employe_Evenement.IdEmploye) AND (T_Employe_Evenement_Temp.IdEvenement = T_Employe_Evenement.IdEvenement) " & _
"SET T_Employe_Evenement_Temp.Participant = True "
db.Execute sSQL, dbFailOnError ' on sélectionne les participants dans la table temporaire
Me.SF_Employes_Evenement.Requery 'on rafraîchit le sous-formulaire
Set db = Nothing ' on libère la variable objet
End Sub |
VII. Sous-formulaire SF_Employes_Evenement
Il est contenu dans le formulaire principal et est basé sur la requête R_Employes_Evenement. Ce sous-formulaire est en mode feuille de données, avec ajout et suppression des données interdits.
Il affiche donc les noms et prénoms des employés, avec pour chacun une case à cocher pour indiquer s'il participe ou pas à l'événement :

VII-A. Liaison entre le formulaire principal et le sous-formulaire
Pour les relier, on définit dans les propriétés du sous-formulaire, IdEvent comme champ père, et IdEvenement comme champ fils.

VII-B. Procédure sur avant MAJ du sous-formulaire
Elle permet de supprimer ou d'ajouter dans la table T_Employe_Evenement un employé pour un événement donné.
Déroulé de la procédure :
- On recherche s'il y a un enregistrement dans la table T_Employe_Evenement avec les identifiants de l'employé et de l'événement.
- Si la case Participant n'est pas cochée, et si un enregistrement a été trouvé dans la table T_Employe_Evement, alors on le supprime.
- Sinon, si la case est cochée, alors on ajoute l'enregistrement dans la table T_Employe_Evenement.
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
| Private Sub Form_BeforeUpdate(Cancel As Integer)
Dim db As DAO.Database ' variable objet pour faire référence à la base en cours
Dim rs As DAO.Recordset ' variable objet pour faire référence au recordset
Set db = CurrentDb ' référence à la base courante
' ouverture du recordset lié à la table T_Employe_Evenement
Set rs = db.OpenRecordset("T_Employe_Evenement", dbOpenDynaset)
' recherche s'il y a un enregistrement dans la table T_Employe_Evenement avec les identifiants de l'employé et de l'événement
rs.FindFirst "IdEmploye=" & Me.IdEmploye & " and IdEvenement=" & Me.IdEvenement
If Not Participant Then ' si la case du participant n'est pas cochée
If Not rs.NoMatch Then ' si un employé a été enregisté pour cet événement dans la table T_Employe_Evenement
rs.Delete ' on supprime l'enregistrement correspondant
End If
Else ' sinon si la case est cochée
' on ajoute les identifiants de l'employé et de l'événement dans la table T_Employe_Evenement
rs.AddNew
rs!IdEmploye = Me.IdEmploye
rs!IdEvenement = Me.IdEvenement
rs.Update
End If
' on ferme et libère les variables objet
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub |