Ce tutoriel est destiné aux lecteurs souhaitant créer dynamiquement des requêtes
paramétrables et ainsi, améliorer leurs systèmes de sélection de critères
dans des requêtes Microsoft Access.
Ce petit tutoriel a pour objectif de vous apprendre à réaliser une
interface simple permettant de saisir le critère d'une requête
paramétrée depuis un formulaire dans lequel une zone de liste
modifiable fera office de paramètre.
Il ne nécessite pas de réelles connaissances en VBA et les points
difficiles abordés ne demandent pas à être maîtrisés mais simplement compris.
La table utilisée en guise d'exemple est issue de la base de données Comptoir.mdb
disponible dans le répertoire Samples d'Office
2. Pourquoi un tel besoin ?
Access permet en effet à l'utilisateur de saisir des critères dans une requête.
Toutefois, il est impossible de formater la saisie, ni même de la contrôler.
Prenons le cas d'une requête sélectionnant un client en fonction de son numéro.
Cette requête serait :
SELECT * FROM Clients
WHERE Code_Client=[Sélectionnez un numéro de client];
Access traduit ce qu'il y a entre crochet en affichant une fenêtre de saisie,
c'est ce que l'on appelle le paramètre :
Toutefois si le numéro à saisir est de type numérique,
rien n'empêche l'utilisateur de saisir des caractères.
Evidemment dans ce cas, la requête ne retournera aucun enregistrement et
dans le pire des cas, un message d'erreur apparaîtra
3. Principe de réalisation
Pour contourner ce problème, il nous faut créer notre propre fenêtre de saisie.
Dans un premier temps, nous allons créer une requête paramétrée
classique qui va nous servir de modèle, le nom du critère
étant quant à lui, écrit entre crochets.
Ensuite, il nous faut créer un formulaire de saisie. Ainsi, nous
pouvons imaginer placer une zone de liste pour le choix d'un pays,
un calendrier pour le choix d'une date, etc les possibilités sont multiples.
Enfin, nous rédigerons une nouvelle requête temporaire basée sur la requête modèle
mais en modifiant les critères entre crochets par leur valeur.
4. Création de la requête modèle
Soit la table Clients suivante :
Tout d'abord, créez une requête permettant d'afficher tous les
clients habitant dans un pays sélectionné.
La requête SQL est donc :
SELECT * FROM Clients
WHERE Pays=[CriterePays];
Testez la requête avec le pays France :
Le résultat est le suivant :
La requête fonctionne, enregistrez-la sous le nom de qryClientParPays_modele.
5. Création du formulaire
Vous allez maintenant créer un nouveau formulaire en mode création
où vous poserez une zone de liste modifiable nommée cboPays
et un bouton nommé btValider.
La zone de liste va permettre de proposer le choix du pays à l'utilisateur.
Il vous faut donc lui définir quelques propriétés :
Origine Source : Table/Requête
Contenu :
SELECT DISTINCT Pays FROM Clients ORDER BY Pays
Cette requête renvoie la liste des pays figurant dans la table
Clients en les triant par ordre alphabétique. Le mot clé DISTINCT
permet de retirer les doublons de la sélection.
Pour que le formulaire se comporte comme une boite de dialogue Windows,
il faut définir ses propriétés Fenêtre Indépendante et Modale à Oui (Onglet : autres).
Après avoir retirer les barres de défilement, le diviseur
d'enregistrements, le sélecteur ainsi que les boutons de déplacements
(Onglet Format) vous obtiendrez aisément quelque chose de comparable à cette fenêtre :
Sauvegardez ce formulaire et nommez-le frmSelectionClientPays.
6. Création de la nouvelle requête
6.1. Déclaration de la référence DAO
La création d'une requête sous Visual Basic implique la manipulation
d'objets de la base de données. Pour cela nous allons utiliser
la librairie DAO qui permet de telles acrobaties.
Pour utiliser cette librairie vous devez ajouter la référence
Microsoft DAO 3.6 à notre projet. Dans l'éditeur VBA, cliquez sur Outils, puis Références.
Cochez la ligne intitulée Microsoft DAO 3.6 Object Library.
6.2. Détails du fonctionnement
Vous disposez maintenant d'une requête nommée qryClientParPays_modele qui contient
le modèle de la requête à créer. Sur le bouton Valider
du formulaire, il vous faut récupérer son code SQL, modifier le critère entre crochets,
puis sauvegarder le tout dans une nouvelle requête.
Enfin, vous devrez exécuter cette requête nouvellement créee afin d'afficher les données voulues.
Un problème majeur se pose alors.
A la première exécution, vous n'allez rencontrer aucune difficulté.
La requête va bien s'enregistrer sous le nom de qryClientParPays.
Mais à la prochaine exécution, lorsque le programme souhaitera
enregistrer la requête, une erreur sera levée.
En effet, le nom qryClientParPays sera déjà utilisé.
Il vous faudra donc procéder à un test d'existence avant d'enregistrer la nouvelle requête sans quoi le programme ne pourra fonctionner.
Soit elle n'existe pas, dans ce cas, il faut la créer. Soit elle existe déjà, et dans ce cas, il faut la modifier
6.3. Comment tester si une requête existe déjà
Pour vérifier qu'une requête existe déjà, il est nécessaire de rédiger une petite fonction à placer dans un module.
Inutile de s'attarder sur le code. L'essentiel est que vous sachiez l'utiliser.
PublicFunction TesteExistenceRequete(strNomRequete AsString) AsBooleanOnErrorGoTo err
Dim Db As DAO.Database
Dim QryTest As DAO.QueryDef
'initialise un objet représentant la base de donnéesSet Db = CurrentDb
'tente d'accéder à la requêteSet QryTest = Db.QueryDefs(strNomRequete)
TesteExistenceRequete = True
err:
EndFunction
En fait, le programme tente d'accéder à la requête dont le nom est
passé en paramètre. S'il y parvient, la fonction retourne True
(Vrai), sinon la fonction retourne son état
d'origine soit False (Faux).
6.4. Modification de la requête modèle
Procédez de la même façon que dans la fonction
ci-dessus pour atteindre la requête modèle et en extraire le code
SQL à l'aide de la propriété SQL de l'objet QueryDef.
Ainsi, dans la propriété Sur Clic du bouton Valider,
créez une procédure événementielle contenant le code suivant :
Dim Db As DAO.Database
Dim QryModele As DAO.QueryDef
Dim strSQLModele AsStringSet Db = CurrentDb
Set QryModele = Db.QueryDefs("qryClientParPays_modele")
strSQLModele = QryModele.SQL
A partir de là, il vous devez modifier le mot [criterepays] de la chaîne
de caractères par la valeur contenue dans la zone de liste.
Suivant le type de champ utilisé par le critère, il faut procéder de différentes manières.
- Le champ est de type texte (Le cas ici) :
Le code SQL devra se présenter de cette manière :
SELECT * FROM Clients
WHERE Pays="FRANCE";
Pour créer une telle chaîne sous VBA, il nous faut donc encadrer la valeur
de la zone de liste par des guillemets, ce que fait la fonction Chr(34).
- Le champ est une date :
Access stocke les dates au format américain (mm/dd/yyyy)
et les dates sont à encadrer par des #.
Exemple :
SELECT * FROM Clients
WHERE DateNaissance=#10/22/1976#
- Le champ est d'un autre type :
Aucune précaution n'est alors nécessaire et vous pouvez écrire le critère directement.
La fonction Nz permet de traiter le cas où la valeur de la zone de liste serait nulle.
Dans ce cas, au lieu de renvoyer NULL, la fonction Nz retourne "" (une chaîne vide).
Nous encadrons ensuite la valeur retournée par cette fonction avec les guillemets fournies par la fonction Chr(34).
Enfin, la fonction Replace effectue le remplacement du critère
par la nouvelle valeur obtenue
6.5. Sauvegarde de la nouvelle requête
A ce stade le code SQL créé est prêt à être enregistré
dans une nouvelle requête nommée qryClientParPays.
Je vous rappelle toutefois que nous devons vérifier que la requête
n'existe pas avant de l'enregistrer dans un nouvel objet DAO,
sans quoi le programme générera un bug.
Le code devient alors :
'Si la requête existe déjà alorsIf TesteExistenceRequete("qryClientParPays") Then'alors modifier le code de la requête
Db.QueryDefs("qryClientParPays").SQL = strSQLModele
'SinonElse'Créer la nouvelle requête
Db.CreateQueryDef "qryClientParPays", strSQLModele
EndIf
J'insiste sur le fait que la méthode CreateQueryDef
possède comme premier paramètre le nom de la requête à
créer et comme second le code représenté par la
chaîne SQL correspondante. Cette méthode créée
et enregistre une nouvelle requête dans la base de données.
6.6. Affichage des données
Il ne reste plus qu'à ouvrir la nouvelle requête pour afficher
le résultat. Pour cela, utilisez la méthode Docmd.OpenQuery.
DoCmd.OpenQuery "qryClientParPays"
Le code devient donc :
PrivateSub btValider_Click()
Dim Db As DAO.Database
Dim QryModele As DAO.QueryDef
Dim strSQLModele AsStringSet Db = CurrentDb
Set QryModele = Db.QueryDefs("qryClientParPays_modele")
strSQLModele = QryModele.SQL
'Effectue le remplacement du critere par la valeur
strSQLModele = Replace(strSQLModele, "[criterepays]", Chr(34) & Nz(cboPays) & Chr(34))
'Si la requête existe déjà alorsIf TesteExistenceRequete("qryClientParPays") Then'alors modifier le code de la requête
Db.QueryDefs("qryClientParPays").SQL = strSQLModele
'SinonElse'Créer la nouvelle requête
Db.CreateQueryDef "qryClientParPays", strSQLModele
EndIf'Ouvre la requête
DoCmd.OpenQuery "qryClientParPays"'Ferme le formulaire
DoCmd.Close acForm, Me.NameEndSub
Afin de parfaire le fonctionnement de cet exemple, vous pouvez ajouter
une routine de gestion d'erreur afin d'alerter l'utilisateur
lors d'un dysfonctionnement.
PrivateSub btValider_Click()
OnErrorGoTo err
Dim Db As DAO.Database
Dim QryModele As DAO.QueryDef
Dim strSQLModele AsStringSet Db = CurrentDb
Set QryModele = Db.QueryDefs("qryClientParPays_modele")
strSQLModele = QryModele.SQL
'Effectue le remplacement du critere par la valeur
strSQLModele = Replace(strSQLModele, "[criterepays]", Chr(34) & Nz(cboPays) & Chr(34))
'Si la requête existe déjà alorsIf TesteExistenceRequete("qryClientParPays") Then'alors modifier le code de la requête
Db.QueryDefs("qryClientParPays").SQL = strSQLModele
'SinonElse'Créer la nouvelle requête
Db.CreateQueryDef "qryClientParPays", strSQLModele
EndIf
DoCmd.OpenQuery "qryClientParPays"'Ferme le formulaire
DoCmd.Close acForm, Me.NameExitSub
err:
MsgBox "Une erreur est survenue", vbCritical, "Sélection d'un client"EndSub
De même, les puristes préféreront utiliser des variables pour
stocker le nom des requêtes. Ce qui pourrait donner :
PrivateSub btValider_Click()
OnErrorGoTo err
Dim Db As DAO.Database
Dim QryModele As DAO.QueryDef
Dim strSQLModele AsStringDim strNomQueryModele AsStringDim strNomQuery AsString
strNomQueryModele = "qryClientParPays_modele"
strNomQuery = "qryClientParPays"Set Db = CurrentDb
Set QryModele = Db.QueryDefs(strNomQueryModele)
strSQLModele = QryModele.SQL
'Effectue le remplacement du critere par la valeur
strSQLModele = Replace(strSQLModele, "[criterepays]", Chr(34) & Nz(cboPays) & Chr(34))
'Si la requête existe déjà alorsIf TesteExistenceRequete(strNomQuery) Then'alors modifier le code de la requête
Db.QueryDefs(strNomQuery).SQL = strSQLModele
'SinonElse'Créer la nouvelle requête
Db.CreateQueryDef strNomQuery, strSQLModele
EndIf
DoCmd.OpenQuery strNomQuery
'Ferme le formulaire
DoCmd.Close acForm, Me.NameExitSub
err:
MsgBox "Une erreur est survenue", vbCritical, "Sélection d'un client"EndSub
7. Visualisation
Pour visualiser le résultat il vous suffit d'ouvrir le formulaire
en mode normal, de sélectionner un pays et de valider.
Le résultat obtenu est bien le même que lorsque vous aviez
saisi le pays en toute lettre au début de ce document.
8. Aller plus loin ...
Vous remarquerez que le mode opératoire ci-dessus vous oblige à
créer autant de requêtes modèles que de requêtes à utiliser au final.
Aussi, à un stade où l'application aura beaucoup été utilisée vous
vous retrouverez avec le double de requêtes que nécessaire. Une
solution consiste alors à stocker le code SQL des requêtes modèles
dans une table locale et de lire cette table afin de créer de
véritables requêtes enregistrées.
Ainsi par exemple nous pourrions créer la table suivante :
Champ
Type
Taille
NomRqt
Texte
50 (Clé primaire)
CodeRqt
Texte
255
DescriptionRqt
Texte
50
Vous enregistrerez cette table sous le nom : tblRequete.
Dans le cas où vous auriez à stocker de grandes requêtes, vous pouvez utiliser le
type Mémo pour le champ CodeRqt.
8.1. Code du bouton Valider
Afin d'utiliser cette table pour lire le SQL des requêtes
il est nécessaire de modifier le code du bouton Valider de la sorte :
PrivateSub btValider_Click()
OnErrorGoTo err
Dim Db As DAO.Database
Dim QryModele As DAO.QueryDef
Dim RstTable As DAO.Recordset
Dim strSQLModele AsStringDim strNomRqt AsString'Instancie la base de donnéesSet Db = CurrentDb
'Ouvre la table des requetes sur l'enregistrement'dont le nom de la requête est "qryClientParPays"
strNomRqt = "qryClientParPays"Set RstTable = Db.OpenRecordset("SELECT * FROM tblRequete WHERE NomRqt=" & Chr(34) & strNomRqt & Chr(34))
'Si la requête est trouvée dans la table,IfNot RstTable.EOF Then'Récupère le code correspondant
strSQLModele = RstTable.Fields("CodeRqt")
'Effectue le remplacement du critere par la valeur
strSQLModele = Replace(strSQLModele, "[criterepays]", Chr(34) & Nz(cboPays) & Chr(34))
'Si un objet requête existe déjà alorsIf TesteExistenceRequete(strNomRqt) Then'alors modifier le code de la requête
Db.QueryDefs(strNomRqt).SQL = strSQLModele
'SinonElse'Créer la nouvelle requête
Db.CreateQueryDef strNomRqt, strSQLModele
EndIf
DoCmd.OpenQuery "qryClientParPays"'Ferme le formulaire
DoCmd.Close acForm, Me.Name'SinonElse'Avertir que le code SQL n'est pas disponible
MsgBox "Impossible de trouver la requête : " & strNomRqt & " dans la table des requêtes"EndIfExitSub
err:
MsgBox "Une erreur est survenue" & vbCrLf & err.Description, vbCritical, "Sélection d'un client"EndSub
Quelques explications :
Pour lire les données d'une table nous devons utiliser des objets de type
Recordset (littéralement : jeu d'enregistrements).
Il s'agit ni plus ni moins du résultat d'une requête et chaque
enregistrement est stocké en mémoire. Lorsque le Recordset
est vide à son ouverture, sa propriété EOF est égale à True.
Cette propriété signifie en fait que l'on se trouve après le dernier
enregistrement, ce qui est logique si le jeu est vide.
Enfin, il est possible d'accéder à un champ du Recordset
pour la ligne courante à l'aide de la syntaxe : RstTable.Fields("Nomduchamp")
Une fois le code SQL lu et stocké dans une variable (strSQLModele),
le principe de création de la requête reste le même que celui
utilisé pour les requêtes modèles tout en gardant à
l'esprit que la requête à créer peut déjà exister.