I. Où en est-on ?▲
Cet article constitue la suite de Contrôler la production de vaches laitières avec Access.
Les données relatives aux contrôles laitiers sont importées, il nous reste à les exploiter :
II. Préliminaires : construire une table tAnalyse avec une ligne par vache▲
Une table dont chaque enregistrement aura cet aspect :
1er temps : recueillir la date de chaque contrôle
2e temps : numéroter les contrôles : le rang 1 pour le contrôle le plus récent, le rang 2 pour l'avant-dernier, 3 pour l'antépénultième et ainsi de suite.
3e temps : trois requêtes (rRangx, où x=1, 2 ou 3) pour extraire les données relatives à chaque rang
4e temps : une requête qui crée une table (tAnalyse) pour juxtaposer tous les morceaux
Remarquez la syntaxe des colonnes des Rx :

Pour le calcul du rapport entre TPx et TBx, on remplace TPx par zéro si sa valeur est Null
et on remplace TBx par 1 si sa valeur est Null.
Ceci pour éviter qu'une erreur (division par zéro) soit levée si l'un de ces deux opérandes a la valeur Null.
Public Function ZeroNullToOne(Ceci As Variant) As Double
If Ceci = 0 Or IsNull(Ceci) Then
ZeroNullToOne = 1
Else
ZeroNullToOne = Ceci
End If
End FunctionOn aurait pu utiliser une simple requête de type Sélection avec les mêmes colonnes.
Cependant comme ces données seront exploitées plusieurs fois et que l'exécution de cette requête est relativement longue (1 s), nous avons préféré une exécution unique (à l'ouverture du formulaire) avec sauvegarde des données dans une table (tAnalyse).
![]() |
Le formulaire fAnalyse est une enveloppe qui contient - pour un éleveur choisi - quatre sous-formulaires juxtaposés. Dans un premier temps, nous allons expliquer chaque composante en détail, nous verrons ensuite comment on les a intégrées dans l'enveloppe. |
III. Le formulaire sfAnalyseDonnesGlobales▲
III-A. Sa source▲
C'est la requête rsfAnalyseDonnesGlobales construite au départ de la table tAnalyse décrite plus haut.
Sur la figure, les repères montrent la correspondance entre les colonnes de la requête et les contrôles du formulaire.
![]() |
![]() |
IV. Le formulaire sfAnalyseMoins100jrs▲
IV-A. Sa source▲
C'est la requête rsfAnalyseMoins100jrs

Elle ramène toutes les colonnes de la table tAnalyse pour autant que :
- l'éleveur correspond à celui choisi ;
- la durée de lactation était inférieure à 100 jours lors du dernier contrôle.
IV-B. Les mises en forme conditionnelles▲
V. Le formulaire sfAnalyseDuTroupeau▲
V-A. Sa source▲
À l'ouverture, le formulaire aura pour source la requête rsfAnalyseDuTroupeauNumCtl.
C'est le même genre de construction que celle décrite plus haut pour le formulaire sfAnalyseDonnesGlobales.
![]() |
Pour éviter une division par zéro. |
V-B. Deux présentations en alternance▲
Un clic sur le bouton déclenche ce code :
Option Compare Database
Option Explicit
Private Sub BtCritere_Click()
If Me.BtCritere.Caption = "Par N° de contrôle" Then
'Changer le libellé du bouton
Me.BtCritere.Caption = "Par N° de lactation"
'Changer la source
Me.RecordSource = "rsfAnalyseDuTroupeauNumLacta"
'Ajuster les contrôles du formulaire
Me.txtCritere.ControlSource = "NumLacta"
Me.etiCritere.Caption = "N°Lacta"
Else
'Changer le libellé du bouton
Me.BtCritere.Caption = "Par N° de contrôle"
'Changer la source
Me.RecordSource = "rsfAnalyseDuTroupeauNumCtl"
'Ajuster les contrôles du formulaire
Me.txtCritere.ControlSource = "NumControle1"
Me.etiCritere.Caption = "N°Ctl"
End If
End SubN.B. La requête rsfAnalyseDuTroupeauNumLacta est identique à rsfAnalyseDuTroupeauNumCtl décrite plus haut, si ce n'est le regroupement qui s'opère sur NumLacta plutôt que sur NumControle1 :

VI. Le formulaire sfAnalyseDetailParVL▲
VI-A. Sa source▲
VI-B. Deux présentations en alternance▲
Un clic sur le bouton déclenche ce code :
Option Compare Database
Option Explicit
Private Sub BtTri_Click()
Dim q As QueryDef
Dim t() As String
If Me.BtTri.Caption = "Par N° de contrôle" Then
'Changer la légende
Me.BtTri.Caption = "Par N° de Lactation"
'Changer le tri de la requête source (rsfAnalyseVLparNumCtl)
'On récupère le SQL de la source d'origine
Set q = CurrentDb.QueryDefs("rsfAnalyseVLparNumCtl")
t = Split(q.SQL, "Order By")
'On modifie l'ordre de tri
Me.RecordSource = t(0) & "ORDER BY tVL.numLacta, tAnalyse.NomBovide;"
'Aménager la présentation du formulaire
Me.txtNumControle1.FontWeight = 400
Me.txtNumLacta.FontWeight = 700
Set q = Nothing
Else
'Changer la légende
Me.BtTri.Caption = "Par N° de contrôle"
'Remettre la requête source d'origine
Me.RecordSource = "rsfAnalyseVLparNumCtl"
'Aménager la présentation du formulaire
Me.txtNumControle1.FontWeight = 700
Me.txtNumLacta.FontWeight = 400
End If
End SubVI-C. Quelques explications en plus▲
VII. Tout en un▲
L'idée, on choisit un éleveur dans une liste déroulante et les quatre formulaires s'affichent ensemble :
VII-A. Construction pas à pas▲
VII-A-1. Une zone de liste dans l'entête▲
VII-A-2. Incorporer les quatre sous-formulaires dans la section Détail▲
Afficher la liste des formulaires, sélectionner un des quatre formulaires et le faire glisser vers la section Détail :
Répéter l'opération pour les trois autres, pour obtenir ceci :
VII-A-3. Au chargement du formulaire, créer la table des données tAnalyse▲
Private Sub Form_Load()
DoCmd.SetWarnings False
DoCmd.OpenQuery "rCreatAnalyse"
DoCmd.SetWarnings True
End SubVII-A-4. Voyons le résultat▲
À ce stade, si nous tentons d'afficher le formulaire, une erreur est levée :

Si nous cliquons sur Débogage, il vient ceci :

Que se passe-t-il ?
Notre formulaire contient quatre sous-formulaires qui ont chacun une source qui se réfère à tAnalyse. Exécuter la requête rCreatAnalyse revient à d'abord supprimer cette table (pour la recréer ensuite avec des données éventuellement plus récentes). Protestation véhémente d'Access qui stoppe le processus.
VII-A-5. Va falloir ruser !▲
Nous allons tout simplement modifier nos sous-formulaires… en supprimant leur source… et en la restituant en temps opportun.
Si nous supprimons les sources des sous-formulaires, nous aurions ceci à l'ouverture de fAnalyse :
Mieux vaut cacher cette présentation peu sympathique !
Plaçons à « Non » la propriété « Visible » des conteneurs des sous-formulaires :
Cette fois, à l'ouverture, ce code est exécuté :
Private Sub Form_Load()
DoCmd.SetWarnings False
DoCmd.OpenQuery "rCreatAnalyse"
DoCmd.SetWarnings True
Me.cboEleveur.SetFocus
Me.cboEleveur.Dropdown
DoCmd.MoveSize 105, 100, 18540, 3000
End SubEt il vient ceci :
Et lorsque l'utilisateur aura choisi l'éleveur qu'il veut analyser, le code suivant se déclenche :
Private Sub cboEleveur_AfterUpdate()
Me.CTNRsfAnalyseDonnesGlobales.Form.RecordSource = "rsfAnalyseDonnesGlobales"
Me.CTNRsfAnalyseDonnesGlobales.Visible = True
Me.CTNRsfAnalyseMoins100jrs.Form.RecordSource = "rsfAnalyseMoins100jrs"
Me.CTNRsfAnalyseMoins100jrs.Visible = True
Me.CTNRsfAnalyseDuTroupeau.Form.RecordSource = "rsfAnalyseDuTroupeauNumCtl"
Me.CTNRsfAnalyseDuTroupeau.Visible = True
Me.CTNRsfAnalyseDetailParVL.Form.RecordSource = "rsfAnalyseVLparNumCtl"
Me.CTNRsfAnalyseDetailParVL.Visible = True
DoCmd.MoveSize 105, 100, 18540, 10515
End SubOn restitue sa source à chacun des sous-formulaires et on le rend à nouveau visible.
VIII. Téléchargement▲
La base de données au format Access 2000 est ici.
(Décompressez l'archive dans un répertoire quelconque.)
IX. Remerciements▲
Ma gratitude à Joël50 qui a pris le temps de m'expliquer en détail les aspects métier de ce tutoriel.
Merci aussi à Malick Seck pour les retouches orthographiques.





































