IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Définition et manipulation de données avec DAO

Image non disponible


précédentsommairesuivant

4. DAO et la structure d'une base de données

4-1. Généralités

Nous abordons là un point fort de DAO à savoir la définition des données (DDL). En effet, l'objet Database donne accès à l'ensemble des tables, des requêtes et des relations d'une base de données Access. Non seulement il est possible de consulter les différents objets mais il est aussi possible de les modifier, d'en ajouter et d'en supprimer. Vous pouvez donc, via votre code VBA, travailler sur la structure de votre fichier comme vous le feriez en mode création dans l'application Access. L'intérêt est bien entendu de pouvoir modifier cette structure sans aucune intervention de l'utilisateur. (Laisser l'utilisateur agir sur les tables directement constitue un réel danger pour votre application). Pour cela, les autres SGBD utilisent généralement le langage SQL (avec des instructions Create, Alter, Drop …). Access ne dispose pas de tant de possibilités avec son langage SQL. Le recours à la programmation VBA devient donc impératif pour réaliser certaines opérations. Nous allons donc voir plus loin dans ce chapitre comment travailler sur les tables, les requêtes et les relations. Mais, dans un premier temps, nous allons aborder le thème des propriétés spécifiques.

4-2. Les propriétés spécifiques

 

4-2-1. Définitions

DAO est un modèle objet. Aussi chaque objet qui le compose possède une liste de propriétés dont chacune d'elles correspond à un objet Property. Vous vous demandez alors quel est l'intérêt de la collection Properties puisque ces propriétés sont disponibles sous VBA avec la syntaxe : Objet.Propriété. Certes Visual Basic liste certaines propriétés mais il ne les propose pas toutes. Ainsi par exemple, les objets TableDef possèdent une propriété Description qui correspond à la description de la table. Cette propriété n'est pas listée dans le récapitulatif des propriétés des objets TableDef, le seul moyen de l'atteindre est d'utiliser la collection TableDef.Properties.

Exemple de propriété

Pourquoi certaines propriétés ne figurent que dans la collection properties ?

Tout d'abord, bon nombre de propriétés de la collection Properties ne sont pas créées jusqu'à ce qu'une valeur ne leur soit affectée. Ainsi, la propriété description de l'objet TableDef n'existe pas si vous n'avez pas encore saisi la description de la table sous Access.

Exemple de propriété

De plus vous pouvez créer vos propres propriétés et les ajouter à la collection Properties de l'objet de votre choix.
Il devient donc impossible de réaliser une liste exhaustive de l'ensemble des propriétés d'un objet DAO.

4-2-2. Liste des propriétés d'un objet Property

Chaque objet Property possède lui aussi un ensemble de propriétés.

4-2-2-1. Inherited

Lecture seule.

Lorsque cette propriété est égale à True, cela signifie que la propriété ne fait pas partie de l'objet même mais qu'elle est héritée d'un autre objet.
Imaginons un objet QueryDef Q1 auquel nous ajoutons une propriété personalisée P1. P1 a été créée pour Q1 donc, la propriété P1.Inherited est égale à False. Si par la suite nous ouvrons un recordset R1 sur Q1, la propriété P1 se retrouvera dans la collection Properties de R1. P1 sera alors héritée et P1.Inherited sera égale à True.

Dans ce cas, ces deux expressions seront vérifiées :

 
Sélectionnez
Q1.Properties("P1").Inherited=False

Et :

 
Sélectionnez
R1.Properties("P1").Inherited=True
4-2-2-2. Name

Cette propriété correspond au nom de la propriété. Celui-ci doit être unique au sein de la collection Properties de l'objet concerné, sans quoi une erreur sera levée lorsque vous tenterez d'ajouter le nouvel objet Property à la collection Properties.

Utilisez des noms précis et en relation avec ce que la propriété modélise. De même éviter d'utiliser des mots réservés par Visual Basic. Ainsi, si la propriété représente une date de vérification, nommez la DateVerification et non pas simplement Date.

4-2-2-3. Type

Il s'agit du type de la propriété. Sa valeur doit être une des constantes suivantes :

Constante Valeur Type
dbBigInt 16 Numérique (haute précision)
dbBinary 9 Binaire de longueur fixe (255 octets maxi)
dbBoolean 1 Booléen
dbByte 2 Octet
dbChar 18 Chaîne de caractère de longueur fixe
dbCurrency 5 Monétaire
dbDate 8 Date/Heure
dbDecimal 20 Décimal
dbDouble 7 Double
dbFloat 21 Réel à virgule flottante
dbGUID 15 Identifiant GUID
dbInteger 3 Entier
dbLong 4 Entier long
dbLongBinary 11 Binaire à longueur variable. (OLE)
dbMemo 12 Mémo
dbNumeric 19 Numérique
dbSingle 6 Réel Simple
dbText 10 Texte à longueur fixe
dbTime 22 Heure
dbTimeStamp 23 TimeStamp
dbVarBinary 17 Binaire à longueur variable (255 octets maxi)
4-2-2-4. Value

Cette propriété correspond à la valeur de l'objet Property. Cette valeur doit être compatible avec le type spécifié dans la propriété Type.

4-2-3. Accéder à une propriété

La collection Properties fonctionne comme toute autre collection DAO. Toutefois, ne connaissant pas le contenu de la collection, il paraît difficile d'accéder à un élément via son index dans la collection. Il nous faut donc spécifier le nom de l'élément à rechercher. Il se pose alors un autre problème qui est que nous ne savons pas si l'objet Property à rechercher existe. En effet, comme nous l'avons vu, par exemple plus haut avec la propriété description des objets Tabledef, celle-ci n'existe que si elle a été initialisée auparavant. Dés lors, nous devons vérifier l'existence de la propriété avant d'y accéder.

Deux méthodes peuvent alors être utilisées. La première consiste à parcourir chacun des objets Property de la collection est de retourner un booléen si l'élément a été trouvé.

 
Sélectionnez
Public Function ExistProperty(ColTProperties As DAO.Properties, _
 strTNom As String) As Boolean
Dim oTmpPrp As DAO.Property
For Each oTmpPrp In ColTProperties
 If oTmpPrp.Name = strTNom Then
  ExistProperty = True
  Exit For
 End If
Next oTmpPrp
End Function

Exemple d'utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
MsgBox ExistProperty(oTbl.Properties, "description")

La seconde méthode consiste à tenter d'accéder à la propriété via son nom et de traiter l'erreur si celle-ci n'existe pas.

 
Sélectionnez
Public Function ExistProperty(ColTProperties As DAO.Properties, _
 strTNom As String) As Boolean
On Error GoTo err
Dim oTmpPrp As DAO.Property
Set oTmpPrp = ColTProperties(strTNom)
ExistProperty = True
err:
End Function

L'utilisation de la fonction reste la même que celle de la précédente. Toutefois, la seconde méthode est plus rapide du fait qu'elle n'utilise pas de boucle.

Dans le cas où vous n'utiliseriez pas une de ses fonctions et que la propriété n'est pas trouvée dans la collection Properties, l'erreur d'exécution 3270 (Propriété non trouvée) sera levée.

Une fois la propriété trouvée, il est possible de connaître sa valeur à l'aide de la propriété value.

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
If ExistProperty(oTbl.Properties, "description") Then
 MsgBox oTbl.Properties("description").Value
Else
 MsgBox "Cette propriété n'existe pas"
End If
oDb.Close
Set oDb=Nothing
Set oTbl=Nothing

4-2-4. Modifier la valeur d'une propriété

Pour modifier la valeur d'un objet Property, il faut tout d'abord que celui ci existe. Nous pouvons donc réutiliser une des fonctions citées plus haut. Ensuite, il suffit de modifier la valeur de l'objet Property en utilisant la propriété Value. Cependant, certaines propriétés sont en lecture seule. Aussi, l'implémentation d'une gestion d'erreur devient impérative.

Voici un exemple de fonction utilisable pour modifier une propriété spécifique d'un objet DAO :

 
Sélectionnez
Public Function ModifProperty(colTProperties As DAO.Properties, _
strTNom As String, strTValeur As Variant) As Boolean
On Error GoTo err
Dim oTmpPrp As DAO.Property
  'Recherche l'élément
  Set oTmpPrp = colTProperties(strTNom)
  'Modifie la valeur
  oTmpPrp.Value = strTValeur
  'Rafraichit la collection
  colTProperties.Refresh
  'L'opération est terminée, renvoyer True
  ModifProperty = True
  Exit Function
'Gestion des erreurs
err:
  Select Case err.Number
    Case 3270
     MsgBox "Propriété non trouvée"
    Case 3421
     MsgBox "Type de propriété incompatible"
    Case Else
     MsgBox "La propriété est en lecture seule"
  End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
ModifProperty oTbl.Properties, "description", "essai"

Quelques remarques sur cette fonction :

TValeur correspond à la nouvelle valeur de la propriété. Le type de cet argument est Variant, pour s'adapter à l'ensemble des types possibles. Toutefois, si vous ne souhaitez modifier des propriétés exclusivement de type String, vous pouvez remplacer le type Variant par String.

La méthode Refresh appliquée à la collection Properties permet de mettre à jour immédiatement la propriété. Si cette méthode n'est pas employée, les modifications ne seront effectives qu'après avoir redémarré votre application.

La fonction renvoie false en cas d'échec de la modification de la propriété.

Il est inutile d'utiliser cette fonction pour les propriétés non spécifiques. En effet, les propriétés proposées par l'explorateur d'objets (F2) sont des propriétés intégrées que vous pouvez modifier avec la syntaxe habituelle Objet.Propriété.

Exemple :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
oTbl.name= "essai"

4-2-5. Création d'une nouvelle propriété

Pour créer une nouvelle propriété, vous devez d'abord vérifier que celle-ci n'existe pas. Ensuite, il suffit d'appliquer la méthode CreateProperty de l'objet concerné pour instancier un nouvel objet Property. A ce stade, la propriété est seulement créée, elle n'est pas encore ajoutée à la liste des propriétés de l'objet. Pour l'ajouter, vous devez utilisez la méthode Append de la collection Properties.

Voici un exemple qui ajoute une propriété nommée "test" de type Integer à la table commande.

 
Sélectionnez
Sub ajout()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Dim oPrp As DAO.Property
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
'verifie que la propriété n'existe pas
If Not ExistProperty(oTbl.Properties, "test") Then
  'Crée la nouvelle propriété
  Set oPrp = oTbl.CreateProperty("test", dbInteger, 0)
  'ajoute la propriété
  oTbl.Properties.Append oPrp
  'Rafraichit la liste des propriétés
  oTbl.Properties.Refresh
End If
'Libération des objets
oDb.Close
Set oTbl=Nothing
Set oDb=Nothing
MsgBox "Propriété ajoutée"
End Sub

La méthode CreateProperty renvoyant un objet de type Property, ce code peut être simplifié par :

 
Sélectionnez
Sub ajout()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = Db.TableDefs("Commande")
'Vérifie que la propriété n'existe pas
If Not ExistProperty(oTbl.Properties, "Test") Then
  'Crée et ajoute la nouvelle propriété
  oTbl.Properties.Append oTbl.CreateProperty("test", dbInteger, 0)
  'Rafraîchit la liste des propriétés
  oTbl.Properties.Refresh
End If
'Libération des objets
oDb.Close
Set oTbl=Nothing
Set oDb=Nothing
MsgBox "Propriété ajoutée"
End Sub

De même, plutôt que de tester l'existence de la propriété, il est possible de tenter de l'ajouter et de gérer l'erreur si une autre propriété portant le même nom existe déjà.

 
Sélectionnez
Sub ajout()
On Error GoTo err
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Commande")
'Crée et ajoute la nouvelle propriété
oTbl.Properties.Append oTbl.CreateProperty("test", dbText, 0)
'Rafraîchit la liste des propriétés
oTbl.Properties.Refresh
MsgBox "Propriété ajoutée"
Exit Sub
'Gère les erreurs
err:
Select Case err.Number
    Case 3367: MsgBox "La propriété existe déjà"
    Case Else: MsgBox "Une erreur inconnue est survenue"
End Select
End Sub

4-3. L'objet TableDef

 

4-3-1. Définitions

La collection TableDefs de l'objet Database regroupe l'ensemble des tables de la base de données. Cette collection inclue aussi les tables systèmes et les tables liées. Chaque objet TableDef qui la compose possède une collection d'objets Field (Champ) et une autre d'objets Index. Ces deux autres types d'objet seront étudiés plus loin dans ce chapitre.

Tout comme c'est le cas en mode création dans l'application Access, il est impossible de modifier la structure d'une table liée en utilisant DAO.

4-3-2. Liste des propriétés

Ci-dessous figure la liste des propriétés appliquables aux objets TableDef.

Certaines de ces propriétés ne sont disponibles en écriture uniquement pour les objets qui n'ont pas encore été ajoutés à la collections Database.TableDefs.

4-3-2-1. Attributes

Type : Long

La propriété Attributes caractérise la table correspondante. Il s'agit d'une (ou une combinaison) des constantes suivantes :

Constante Signification
dbAttachExclusive Utilisable uniquement sur une table liée. Cette valeur indique que la table liée est ouverte en mode exclusif.
dbAttachSavePWD Utilisable uniquement sur une table liée. Avec cette constante, les informations de Login utilisées pour lier la table sont sauvegardées.
dbSystemObject Indique une table système.
dbHiddenObject Indique une table cachée.
dbAttachedTable Indique une table liée via le moteur Jet.
dbAttachedODBC Indique une table liée via ODBC.
4-3-2-2. Connect

Type : String

Il s'agit de la chaîne de connexion utilisée pour une table liée. Sa valeur est nulle pour une table non liée. Cette chaine de connexion prend en compte plusieurs paramètres comme par exemple le nom de l'utilisateur, son mot de passe, etc ... Vous pouvez la consulter visualisant les propriétés de votre table en mode création.

Cette propriété est en lecture seule pour les objets TableDef présents dans la collection Database.TableDefs

4-3-2-3. DataCreated

Lecture seule.

Type : Date

La propriété DataCreated renvoie la date de création de la table.

4-3-2-4. Fields

Type : Collection

Cette collection regroupe l'ensemble des champs de la table. Vous pouvez les modifier, en ajouter et en supprimer.

4-3-2-5. Indexes

Type : Collection

Collection regroupant l'ensemble des index de la table. Vous pouvez les modifier, en ajouter et en supprimer.

4-3-2-6. LastUpdated

Lecture seule.

Type : Date

La propriété LastUpdated retourne la date et l'heure de la dernière modification apportée à la structure de la table concernée.

4-3-2-7. Name

Type : String

La prorpiété Name correspond au nom de la table. Ce nom doit être unique au sein de la collection TableDefs de l'objet Database.

Vous trouverez plus loin dans ce chapitre (section 4.3.4.2) un exemple montrant comment renommer une table dans une base de données.

4-3-2-8. Properties

Type : Collection

La prorpiété Properties est une collection qui regroupe l'ensemble des objets Property de l'objet TableDef.

4-3-2-9. RecordCount

Lecture seule.

Type : Long

La propriété RecordCount renvoie le nombre d'enregistrements de la table.

4-3-2-10. SourceTableName

Type : String

Il s'agit du nom d'origine de la table liée. Si l'objet TableDef n'est pas une table liée, cette propriété est en lecture seule et sa valeur est égale à "".

4-3-2-11. Updatable

Lecture seule.

Type : Boolean

La propriété de Updatable définit si les données provenant de la table peuvent être modifiées.

4-3-2-12. ValidationRule

Type : String

La propriété ValidationRule définit la règle de validation qui sera utilisée pour valider les données lors d'une mise à jour (Insert compris).

Ainsi, pour obliger l'utilisateur a remplir le champ MonChamp lorsque que le champ Booleen MaCase est coché, nous aurions la règle suivante :

 
Sélectionnez
([MonChamp] IS NOT NULL AND [MaCase]) OR NOT ([MaCase])
4-3-2-13. ValidationText

Type : String

La propriété ValidationText correspond au message qui sera affiché lors d'une mise à jour si les données ne vérifient pas la règle spécifiée dans ValidationRule.

4-3-3. Liste des méthodes

4-3-3-1. CreateField

La méthode CreateField crée et retourne un nouvel objet Field qui sera à ajouter par la suite à la collection Database.Tabledefs.

Syntaxe :

 
Sélectionnez
Set field = TableDef.CreateField (name, type, size)

Cette méthode sera traitée plus en détail dans la sous partie dédiée aux objets Field.

4-3-3-2. CreateIndex

La méthode CreateIndex crée un nouvel Index dans la table. Cet Index devra ensuite être ajouté à la collection Indexes de l'objet TableDef pour prendre effet.

Syntaxe :

 
Sélectionnez
Set index = tabledef.CreateIndex (name)
4-3-3-3. CreateProperty

La méthode CreateProperty crée et retourne un nouvel objet Property qu'il vous faudra ensuite ajouter à la collection Properties de l'objet TableDef.

La syntaxe de la méthode est celle illustrée dans la partie : Les propriétés spécifiques.

4-3-3-4. OpenRecordset

La méthode OpenRecordset ouvre un recordset contenant l'ensemble des enregistrements de la table.

Syntaxe :

 
Sélectionnez
Set recordset = object.OpenRecordset (type, options, lockedits)
4-3-3-5. RefreshLink

La méthode RefreshLink n'est utilisable que sur des tables liées. Elle permet, aprés avoir modifié la propriété Connect, de rafraîchir les informations de connexion entre la table attachée et la source de données.

Syntaxe :

 
Sélectionnez
tabledef.RefreshLink

4-3-4. Opérations sur les tables d'une base de données

4-3-4-1. Lister les tables d'une base de données

Je vous propose quelques codes utiles pour lister l'ensemble des tables d'une base de données. Nous ferons la distinction entre les tables systèmes et les tables liées. En effet, la propriété Attributes est un masque binaire. L'opérateur AND entre une constante recherchée et la valeur de la propriété effectue une opération binaire renvoyant un nombre égal à 0 si la constante n'est pas utilisée dans la propriété. Ainsi, pour savoir si une table est une table système, il est possible d'utiliser :

 
Sélectionnez
If monObjet.Attributes AND dbSystemObjet then .....

Pour lister, l'ensemble des tables systèmes, il suffit donc de parcourir un à un les éléments de la collection TableDefs et de de vérifier si l'expression booléene est vraie ou non.

 
Sélectionnez
Sub Lister()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
'Pour chaque table
For Each oTbl In oDb.TableDefs
 'Si la table est système alors afficher son nom
 If oTbl.Attributes And dbSystemObject Then MsgBox oTbl.Name
Next oTbl
End Sub

Si nous souhaitons lister les tables liées, nous aurons :

 
Sélectionnez
Sub Lister()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
'Pour chaque table
For Each oTbl In oDb.TableDefs
 'Si la table est liée alors afficher son nom
 If oTbl.Attributes And dbAttachedTable Then MsgBox oTbl.Name
Next oTbl
End Sub

Pour réunir les deux listes, il suffit d'utiliser l'opérateur logique OR (Ou). Ainsi, le code suivant affichera les tables systèmes ainsi que les tables liées.

 
Sélectionnez
Sub Lister()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
'Pour chaque table
For Each oTbl In oDb.TableDefs
 'Si la table est liée ou système alors afficher son nom
 If oTbl.Attributes And (dbAttachedTable Or dbSystemObject) Then MsgBox oTbl.Name
Next oTbl
End Sub

Ce code n'est pas à confondre avec :

 
Sélectionnez
Sub Lister()
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
'Pour chaque table
For Each oTbl In oDb.TableDefs
 'Si la table est liée et système alors afficher son nom
 If oTbl.Attributes And (dbAttachedTable And dbSystemObject) Then MsgBox oTbl.Name
Next oTbl
End Sub

En effet ce dernier liste les tables qui sont à la fois systèmes et liées.

4-3-4-2. Renommer une table

Renommer une table est une opération extrémement simple. Toutefois, il ne faut pas oublier que le nom de la table doit être unique. Aussi, il faudra implémenter une gestion d'erreur lors d'une telle opération.

Les objets Relations dépendants de la table renommées sont automatiquement mis à jour par le moteur Jet.

 
Sélectionnez
Private Function RenommerTable(oBaseDeDonnees As DAO.Database, strAncienNom As String, _
 strNouveauNom As String) As Boolean
 On Error GoTo err
 Dim oTbl As DAO.TableDef
 'Cherche la table à modifier
 Set oTbl = oBaseDeDonnees.TableDefs(strAncienNom)
 'Renomme la table
 oTbl.Name = strNouveauNom
 'Rafraîchit la collection
 oBaseDeDonnees.TableDefs.Refresh
 'Renvoie true
 RenommerTable = True
Exit Function
err:
Select Case err.Number
  Case 3265: MsgBox "Impossible de trouver la table à renommer"
  Case 3010: MsgBox "La table " & strNouveauNom & " existe déjà"
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
If RenommerTable(oDb, "Commande", "Commande_Archive") Then
 MsgBox "Table renommée avec succès"
End if
4-3-4-3. Supprimer une table

La suppression d'une table nécessite quelques précautions. Hormis le fait que la table doit exister pour être supprimer, il ne faut pas perdre de vue que la suppression d'une table engendre la suppression des relations qui en dépendent. Aussi dans le cas d'une mauvaise manipulation, vous risquez de supprimer des objets Relations sans vous en rendre compte. Dans ce cas, je préconise d'envoyer un message à l'utilisateur lui demandant confirmation. Il nous faut donc savoir si la table fait partie d'un objet relation ou non. Pour cela, nous utiliserons la fonction ExistRelation suivante :

 
Sélectionnez
Private Function ExistRelation(oBaseDeDonnees As DAO.Database, _
  strNomTable As String) As Integer
Dim oRlt As DAO.Relation
'Pour chaque objet relation
For Each oRlt In oBaseDeDonnees.Relations
  'Si la table est présente dans la relation
  If oRlt.Table = strNomTable Or oRlt.ForeignTable = strNomTable Then _
    ExistRelation = ExistRelation + 1
Next oRlt
End Function

Cette fonction renvoie le nombre de relations dépendantes de la table passée en paramètre.

Nous pouvons donc écrire la fonction de suppression de table suivante :

 
Sélectionnez
Private Function SupprimerTable(oBaseDeDonnee As DAO.Database, _
 strNomTable As String)
On Error GoTo err
 Dim intNbRlt As Integer
 'Compte le nombre de relations dépendantes
 intNbRlt = ExistRelation(oBaseDeDonnee, strNomTable)
 'Si la table est présente dans au moins une relation
 If NbRlt <> 0 Then
   'si l'utilisateur ne souhaite pas continuer, sortir de la fonction
   If MsgBox("La table " & strNomTable & " est utilisée dans " & _
     intNbRlt & " relation(s)." & vbCrLf & "La suppression de cette table entrainera la" & _
     " suppression de cette(ces) relation(s)." & vbCrLf & vbCrLf & "Voulez vous continuer ?", _
     vbQuestion + vbYesNo, "Suppression de table") = vbNo Then Exit Function
 End If
 'Supprime la table
 oBaseDeDonnee.TableDefs.Delete (strNomTable)
 'Rafraichit la collection
 oBaseDeDonnee.TableDefs.Refresh
 'Renvoie true
 SupprimerTable = True
 Exit Function
err:
'Gére les erreurs
Select Case err.Number
  Case 3265: MsgBox "Impossible de trouver la table à supprimer"
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Set oDb = CurrentDb
If SupprimerTable(oDb, "Commande") Then
 MsgBox "Table supprimée"
End If
4-3-4-4. Créer une nouvelle table

La création d'une table n'ayant que peu d'intéret sans la création de champs, je vous propose d'aborder avant les parties concernant les champs et les index puis de revenir ensuite sur cet exemple.

Dans cet exemple, nous allons créer une table nommée Client possédant trois champs :

  • IDClient Clé primaire. Type : Long, numéro automatique
  • NomClient Type : Texte. Longueur : 15
  • PrenomClient Type : Texte. Longueur : 25

Voici le code correspondant :

 
Sélectionnez
Sub CreerTable()
Dim oDb As DAO.Database
Dim oNouvelleTable As DAO.TableDef
Dim oChamp As DAO.Field
Dim oIndex As DAO.Index
'Instancie la base de données
    Set oDb = CurrentDb
'Crée la nouvelle table
    Set oNouvelleTable = oDb.CreateTableDef("Clients")
'Crée le champ IDClient
    Set oChamp = oNouvelleTable.CreateField("IDClient", dbLong)
'Définit le champ en numero_auto
    oChamp.Attributes = dbAutoIncrField
'Ajoute le champ à la table
    oNouvelleTable.Fields.Append oChamp
'Crée le champ nomClient et l'ajoute
    oNouvelleTable.Fields.Append oNouvelleTable.CreateField("NomClient", _
        dbText, 15)
'Crée le champ PrenomClient et l'ajoute
    oNouvelleTable.Fields.Append oNouvelleTable.CreateField("PrenomClient", _
        dbText, 25)
'définit la clé primaire sur l'IDClient
    Set oIndex = oNouvelleTable.CreateIndex("PK_IDClient")
    oIndex.Primary = True
    oIndex.Fields.Append oIndex.CreateField("IdClient")
'Ajoute l'index à la table
    oNouvelleTable.Indexes.Append oIndex
'Ajoute la table à la base de données
    oDb.TableDefs.Append oNouvelleTable
    
'Libère les variables
oDb.Close
Set oIndex = Nothing
Set oChamp = Nothing
Set oNouvelleTable = Nothing
Set oDb = Nothing
End Sub

4-4. L'objet Field

 

4-4-1. Définition

Un objet Field représente un champ d'une table ou d'une requête dans une base de données. DAO offre de nombreuses possibilités concernant les objets Field. Il est ainsi possible de créer des champs, d'en supprimer et de les modifier aussi simplement que vous le feriez dans le mode création d'Access. En effet, il existe une réelle analogie entre la liste des propriétés des objets Field et la fenêtre de modification de la structure d'une table. Chaque zone de texte de cette fenêtre correspond à une propriété de l'objet Field.

Je vous propose tout d'abord d'effectuer un tour d'horizon de l'ensemble des propriétés et des méthodes de cet objet, avant de revenir sur des exemples plus spécifiques.

4-4-2. Liste des propriétés

Vous retrouverez ci-dessous l'ensemble des propriétés des objets Field dans la liste suivante.

Certaines de ces propriétés ne sont disponibles en écriture uniquement pour les objets qui n'ont pas encore été ajoutés à la collections TableDef.Fields.

4-4-2-1. AllowZeroLength

Type : Boolean

La propriété AllowZeroLength indique si un champ texte accepte les chaines vides. Si la valeur de cette propriété est égale à True, ces valeurs sont acceptées. Dans le cas échéant, une erreur sera levée lorsque que vous tenterez de mettre à jour les données.

4-4-2-2. Attributes

Type : Long

La propriété Attributes définit les options du champ. Il s'agit d'une une (ou une combinaison) des constantes suivantes :

Constante Signification
dbAutoIncrField Correspond à un champ de type Numéro Auto. A n'utiliser que sur les champs de type numérique.
dbDescending Dans le cas où le champ fait parti d'un index, cette valeur permet d'inverser l'ordre de tri.
dbFixedField Champ de taille fixe. Valeur par défaut des champs de type numérique.
dbHyperlinkField Gère les liens hypertext dans un champs mémo.
dbSystemField Le champ stocke des informations de duplication pour les duplicatas.
Un tel champ ne peut être supprimé.
dbUpdatableField Le valeur du champ peut être modifiée.
dbVariableField Le champ peut être modifié. A n'utiliser que sur les champs de type texte.
4-4-2-3. CollatingOrder

Type : Long

Il s'agit de la méthode utilisée par le moteur pour comparer les valeurs du champ si celui-ci est de type texte.

Les valeurs possibles sont :

Constante Signification
dbSortGeneral Général (Français, anglais ...)
dbSortArabic Arabe
dbSortChineseSimplified Chinois simplifié
dbSortChineseTraditional Chinois traditionnel
dbSortCyrillic Russe
dbSortCzech Tchèque
dbSortDutch Néerlandais
dbSortGreek Grec
dbSortHebrew Hébreu
dbSortHungarian Hongrois
dbSortIcelandic Islandais
dbSortJapanese Japonais
dbSortKorean Coréen
dbSortNeutral Neutre
dbSortNorwDan Norvégien et danois
dbSortPDXIntl Paradox International
dbSortPDXNor Paradox norvégien ou danois
dbSortPDXSwe Paradox suédois ou finnois
dbSortPolish Polonais
dbSortSlovenian Slovène
dbSortSpanish Espagnol
dbSortSwedFin Suédois ou finnois
dbSortThai Thaï
dbSortTurkish Turc
dbSortUndefined Indéfini
4-4-2-4. DataUpdatable

Lecture seule.

Type : Boolean

Lorsque la propriété DataUpdatable est égale à False, il vous est impossible de modifier les valeurs de ce champ. C'est notamment le cas d'un champ auto-incrémenté.

4-4-2-5. DefaultValue

Type : Variant

Cette propriété correspond à la valeur par défaut du champ. Elle est en lecture/écriture pour les champs issus de TableDef et en lecture seule pour ceux issus de QueryDef et Recordset. Elle n'est pas prise en charge par les objets Fields des collections Database.Indexes et Database.Relations.

Pour un champ de type Long vous pouvez attribuer à cette propriété la valeur : GenUniqueID(). Ainsi à chaque nouvel enregistrement ajouté, le moteur Jet attribue une valeur unique et aléatoire au nouvel enregistrement. Cette valeur reproduit le comportement d'une numérotation automatique aléatoire.

4-4-2-6. FieldSize

Lecture seule.

Type : Long

La propriété FieldSize, correspond à la taille utilisée dans la base par un champ de type binaire ou mémo.
Cette valeur est exprimée en nombre de caractères pour un champ mémo et en nombre d'octet pour un champ de type binaire.

4-4-2-7. ForeignName

Type : String

La propriété ForeignName n'est à utiliser que pour les champs d'un objet de type Relation. Elle définit le nom du champ dans la table externe de la relation (clé étrangère). Cette propriété est en lecture/écriture dans le cas d'objets Relation non ajoutés à la collection Database.Relations et en lecture seule pour les objets déjà présents au sein de cette même collection.

4-4-2-8. Name

Type : String

Il s'agit du nom du champ. Le nom d'un champ doit être unique au sein d'une table.

4-4-2-9. OrdinalPosition

Type : Integer

La propriété OrdinalPosition permet de définir la position du champ dans la collection Fields de l'objet TableDef. Le premier élément possède la position 0. Cette position conditionne l'ordre d'apparition des champs dans une requête SELECT * FROM ... Notons que si deux champs possèdent la même valeur pour la propriété OrdinalPosition, ces champs sont alors classés par ordre alphabétique.
Cette propriété est en lecture seule pour les objets QueryDef.

4-4-2-10. Properties

Type : Collection

Collection regroupant l'ensemble des objets Property de l'objet Field.

4-4-2-11. Required

Type : Boolean

La propriété Required indique si le champs accepte les valeurs nulles. Si cette propriété est égale à True, une erreur sera levée si vous tentez d'affecter une valeur nulle à ce champ.

4-4-2-12. Size

Type : Integer

La propriété Size définit la taille du champ comme vous pouvez le faire en mode création. Cette propriété n'est en lecture écriture que pour les objets Field qui n'ont pas encore été ajoutés à la collection Fields.

4-4-2-13. SourceField

Lecture seule.

Type : String

Cette propriété en lecture seule indique dans un objet QueryDef le nom d'un champ et non son alias. Soit une requête nommée R01 :

 
Sélectionnez
SELECT NomClient as LeNom FROM Client

Le code suivant affichera LeNom :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oQR As DAO.QueryDef
Set oDb = CurrentDb
Set oQR = oDb.QueryDefs("R01")
MsgBox oQR.Fields(0).Name

Cet autre code affichera NomClient :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oQR As DAO.QueryDef
Set oDb = CurrentDb
Set oQR = oDb.QueryDefs("R01")
MsgBox oQR.Fields(0).SourceField

Nous pouvons donc écrire la fonction suivante qui retourne le nom d'un champ en fonction de son alias :

 
Sélectionnez
Function NomChamp(oBaseDeDonnee As DAO.Database, strRequete As String, strAlias As String) As String
On Error GoTo err
Dim Qdf As DAO.QueryDef
Set Qdf = oBaseDeDonnee.QueryDefs(strRequete)
NomChamp = Qdf.Fields(strAlias).SourceField
Exit Function
'Gère les erreurs
err:
Select Case err.Number
 Case 3265
  'Si qdf est vide alors la requete n'existe pas
  If Qdf Is Nothing Then
    MsgBox "Impossible de trouver la requête " & strRequete
  Else
    
    MsgBox "Impossible de trouver le champ " & strAlias & " dans la requete " & _
    strRequete
  End If
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Set oDb = CurrentDb
MsgBox NomChamp(oDb, "R01", "LeClient")
4-4-2-14. SourceTable

Lecture seule.

Type : String

Cette propriété repose sur le même principe que la précédente. Elle permet de savoir de quelle table provient un champ d'une requête.

Ainsi, avec l'exemple précédent, voici comment connaître la table d'origine du champ LeClient :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oQR As DAO.QueryDef
Set oDb = CurrentDb
Set oQR = oDb.QueryDefs("R01")
MsgBox oQR.Fields(0).SourceTable
4-4-2-15. Type

Type : Integer

Cette propriété définit le type du champ. Il s'agit d'une des constantes suivantes :

Constante Valeur Type
dbBigInt 16 Numérique (haute précision)
dbBinary 9 Binaire de longueur fixe (255 octets maxi)
dbBoolean 1 Booléen
dbByte 2 Octet
dbChar 18 Chaîne de caractère de longueur fixe
dbCurrency 5 Monétaire
dbDate 8 Date/Heure
dbDecimal 20 Décimal
dbDouble 7 Double
dbFloat 21 Réel à virgule flottante
dbGUID 15 Identifiant GUID
dbInteger 3 Entier
dbLong 4 Entier long
dbLongBinary 11 Binaire à longueur variable. (OLE)
dbMemo 12 Mémo
dbNumeric 19 Numérique
dbSingle 6 Réel Simple
dbText 10 Texte à longueur fixe
dbTime 22 Heure
dbTimeStamp 23 TimeStamp
dbVarBinary 17 Binaire à longueur variable (255 octets maxi)
4-4-2-16. ValidationRule

Type : String

La propriété ValidationRule définit la règle de validation qui sera utilisée pour valider les données lors d'une mise à jour (Insert compris).

4-4-2-17. ValidationText

Type : String

La propriété ValidationText correspond au message qui sera affiché lors d'une mise à jour si les données ne vérifient pas la règle spécifiée dans ValidationRule.

4-4-2-18. Value

Type : Variant

Pour un objet Recordset, la propriété Value retourne la valeur du champ pour l'enregistrement courant.

4-4-2-19. VisibleValue

Type : Variant

Cette propriété est utilisable uniquement dans un environnement ODBCDirect. Elle renvoit la valeur actuelle d'un champ lors d'un traitement par lot. Cette valeur est donc plus récente que celle retournée par la propriété OriginalValue

4-4-3. Liste des méthodes

4-4-3-1. AppendChunk

La méthode AppendChuk permet d'ajouter des données à la fin d'un champ de type mémo ou binaire. (Collection Recordsets exclusivement) Elle évite notamment de lire le début du champ, de concaténer et de renvoyer la nouvelle valeur. En utilisant la méthode AppendChunk, vous limiter donc le traffic d'informations entre la base de données et le moteur Jet.

Syntaxe :

 
Sélectionnez
field.AppendChunk source

L'argument source est de type Variant.

4-4-3-2. CreateProperty

La méthode CreateProperty crée et retourne un nouvel objet Property qu'il vous faudra ensuite ajouter à la collection Properties de l'objet Field.

La syntaxe de la méthode est celle illustrée dans la partie : Les propriétés spécifiques.

4-4-3-3. GetChunk

La méthode GetChunk s'applique aux champs des objets Recordset. Elle renvoie le contenu (ou une partie) d'un champ mémo ou binaire dans une variable de type String.

Syntaxe :

 
Sélectionnez
Set variable = field.GetChunk (offset, numbytes)

Le paramètre offset de type Long correspond à la position du caractère (ou de l'octet) à partir duquel doit commencer la lecture.

L'argument numbytes détermine le nombre de caractères (ou d'octet) à lire.

4-4-4. Opérations sur les champs d'un objet TableDef

Seul l'objet TableDef permet de modifier les éléments de sa collection Field. Je vous propose plusieurs exemples afin d'illustrer l'étendue des possibilités qu'offre DAO.

4-4-4-1. Renommer un champ

Tout comme c'était déjà le cas avec les tables, cette opération nécessite que le nouveau nom soit unique dans la collection Fields de l'objet TableDef.

Les objets Relation et Index utilisant ce champ sont automatiquement mis à jour par le moteur Jet.

 
Sélectionnez
Private Function RenommerChamp(oBaseDeDonnees As DAO.Database, strNomTable As String, _
  strAncienNomChamp As String, strNouveauNomChamp As String) As Boolean
 
On Error GoTo err
 Dim Tbl As DAO.TableDef
 'Récupère la table
 Set Tbl = oBaseDeDonnees.TableDefs(strNomTable)
 'Renomme le champ
 Tbl.Fields(strAncienNomChamp).Name = strNouveauNomChamp
 'Renvoie True
 RenommerChamp = True
 'Gère les erreurs
 Exit Function
err:
Select Case err.Number
 'Si impossible de trouver l'élément dans la collection
 Case 3265
  'Si tbl=nothing alors tbl est la cause de l'erreur
  If Tbl Is Nothing Then
    MsgBox "Impossible de trouver la table : " & strNomTable
  Else
    MsgBox "Impossible de trouver le champ : " & strAncienNomChamp
  End If
  Case 3010, 3191: MsgBox "Le champ " & strNouveauNomChamp & " existe déjà"
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Set oDb = CurrentDb
If RenommerChamp (oDb, "Commande", "Reference", "Nouvelle_Reference") then
  msgbox "Opération terminée"
End if
4-4-4-2. Supprimer un champ

La suppression d'un champ est trés délicate. En effet, plusieurs erreurs peuvent être levées au cours de cette opération :

  • 3265 : Le champ n'existe pas
  • 3280 : Le champ est utilisé par un index
  • 3303 : Le champ est utilisé dans une ou plusieurs relations

L'erreur 3303 est difficile à maitriser car même si l'erreur est levée, le champ est quand même supprimé. Toutefois les relations sont toujours présentes et si vous rajoutez par la suite le champ supprimé, la relation va "renaître", ce qui provoque un comportement inattendu.

Il nous faut donc savoir si le champ est l'objet de relations.

 
Sélectionnez
Private Function ChampDansRelation(oBaseDeDonnees As DAO.Database, _
 strNomTable As String, strNomChamp As String) As Integer
On Error GoTo err
 Dim bolUtilise As Boolean
 Dim oRlt As DAO.Relation
 Dim oFld As DAO.Field
  'Parcours les relations
  For Each oRlt In oBaseDeDonnees.Relations
   bolUtilise = False
   If oRlt.Table = strNomTable Then
     'Pour chaque champ, contrôle le nom du champ
     For Each oFld In oRlt.Fields
       If oFld.Name = strNomChamp Then bolUtilise = True
     Next oFld
  ElseIf oRlt.ForeignTable = strNomTable Then
    'pour chaque champ, contrôle le nom étranger
    For Each oFld In oRlt.Fields
       If oFld.ForeignName = strNomChamp Then bolUtilise = True
    Next oFld
  End If
  'Si la relation utilise le champ, on incremente le resultat
  If bolUtilise Then ChampDansRelation = ChampDansRelation + 1
 Next oRlt
Exit Function
err:
ChampDansRelation = -1
End Function

Cette fonction renvoie le nombre de relations qui utilisent le champ passé en paramètre.

Utilisation :

 
Sélectionnez
Dim oDb as DAO.Database
Set oDb=CurrentDB
MsgBox ChampDansRelation(oDb, "Commande", "Reference")

Cet exemple affichera le nombre de relations utilisant le champ Reference de la table Commande.

La fonction retourne -1 si une erreur est survenue.

Nous pouvons donc écrire la fonction de suppression suivante :

 
Sélectionnez
Private Function SupprimerChamp(oBaseDeDonnees As DAO.Database, strNomTable As String, _
  strNomChampASupprimer As String) As Boolean
 
On Error GoTo err
 Dim oTbl As DAO.TableDef
 'Verifie que le champ ne fait pas partie d'une relation
 If ChampDansRelation(oBaseDeDonnees, strNomTable, strNomChampASupprimer) = 0 Then
  'Récupère la table
   Set oTbl = oBaseDeDonnees.TableDefs(strNomTable)
  'Supprime le champ
  oTbl.Fields.Delete (strNomChampASupprimer)
  'Renvoie True
  SupprimerChamp = True
 Else
  MsgBox "Impossible de supprimer le champ, il est utilisé par une ou plusieurs relations"
 End If
Exit Function
err:
Select Case err.Number
 'Si impossible de trouver l'élément dans la collection
 Case 3265
  'Si tbl=nothing alors tbl est la cause de l'erreur
  If Tbl Is Nothing Then
    MsgBox "Impossible de trouver la table : " & strNomTable
  Else
    MsgBox "Impossible de trouver le champ : " & strNomChampASupprimer
  End If
  Case 3280: MsgBox "Impossible de supprimer un champ utilisé par un index"
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
SupprimerChamp oDb, "Commande", "Reference"
4-4-4-3. Créer un champ

La création d'un champ se décompose en trois étapes :

Etapes de création d'un champ

Un exemple sera certainement plus parlant que de longues phrases, je vous propose de créer un champ AdresseClient dans une table nommée Client.
Ce champ aura pour cractéristiques :

  • Type : Texte
  • Longueur : 120 caractères
  • Chaine vide autorisée : Non
  • Null interdit : Oui
 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Dim oFld As DAO.Field
Set oDb = CurrentDb
Set oTbl = oDb.TableDefs("Client")
'Etape 1 : Créer le champ
Set oFld = oTbl.CreateField("AdresseClient", dbText, 120)
'Etape 2 : Définit les propriétés
oFld.AllowZeroLength = False 'Chaine vide autorisée : Non
oFld.Required = True         'Null interdit : Oui
'Etape 3 : Ajout du champ à la table
oTbl.Fields.Append Fld
'Rafraichit la collection
oTbl.Fields.Refresh

Il est important de respecter l'ordre de chaque étape. Si vous ajouter le champ à la collection Fields avant de lui avoir affecter l'ensemble de ses propriétés, vous vous apercevrez que certaines de ces propriétés ne sont plus accessibles en écriture.

Nous n'avons pas traité ici la gestion d'erreur. Celle ci exactement la même que pour la fonction qui renomme un champ. En effet, le plus grand risque pour ce programme est d'utiliser un nom de champ qui existe déjà.

4-4-4-4. Dupliquer un champ

Je vous propose ici un code qui peut s'avérer utile lorsque vous souhaiter créer un champ identique à un autre. Le principe est simple, nous partons de deux objets Field. Le premier sera utilisé comme source du clonage. Nous allons donc parcourir chaque propriété (exceptée la propriété Name) du champ source et les réécrire dans le champ de destination.

Certaines de ces propriétés risquent d'être en lecture seule. Il nous faut donc déleguer la lecture et l'écriture des propriétés à une autre procédure afin de ne pas faire stopper le programme à la première erreur rencontrée.

 
Sélectionnez
Public Sub CloneChamp(oFldSource As DAO.Field, oTableDestination As DAO.TableDef, _
  strNouveauNom As String)
On Error GoTo err
Dim oFld As DAO.Field
Dim oPrp As DAO.Property
'Crée le champ
Set oFld = oTableDestination.CreateField(strNouveauNom)
'Pour chaque propriete
For Each oPrp In oFldSource.Properties
 'copier la propriété s'il ne s'agit pas du nom
 If oPrp.Name <> "Name" Then _
        EcrirePropriete oPrp, oFld
Next oPrp
'Ajoute le champ
oTableDestination.Fields.Append oFld
Exit Sub
err:
Select Case err.Number
   Case 3010, 3191: MsgBox "Le champ " & strNouveauNom & " existe déjà"
   Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Sub


Private Sub EcrirePropriete(oPropriete As DAO.Property, _
  oChampDestination As DAO.Field)
On Error GoTo err
'Copie la propriété
With oChampDestination.Properties
 .Item(oPropriete.Name) = oPropriete.Value
 .Refresh
End With
'On ne traite pas les erreurs
err:
End Sub

Pour dupliquer le champ AdresseClient de la table Client dans la table SauvegardeClient nous utiliserons :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTblSource As DAO.TableDef, oTblDestination As DAO.TableDef
Dim oFld As DAO.Field
Set oDb = CurrentDb
'Stocke les deux tables
Set oTblSource = oDb.TableDefs("Client")
Set oTblDestination = oDb.TableDefs("SauvegardeClient")
'Stocke le champ
Set oFld = oTblSource.Fields("AdresseClient")
'Clone le champ
CloneChamp oFld, oTblDestination, "AdresseClient2"

4-5. L'objet Index

 

4-5-1. Définitions

Nous n'allons pas reprendre ici la définition d'un index. Je vous renvoie pour cela vers d'autres tutoriels de ce site. Toutefois, rappelons sommairement qu'un index permet de faciliter l'accès aux données dans une table lors d'une recherche et qu'il permet aussi de poser des contraintes sur les données (Notion de clé primaire et de doublons).

Un index peut être composé d'un ou plusieurs champs, il possède donc une collection Fields recensant ces champs.

4-5-2. Liste des propriétés

4-5-2-1. DistinctCount

Lecture seule.

Type : Long

La propriété DistinctCount renvoie le nombre de valeurs uniques présentes dans la table. Dans le cas d'un index mono-champ, il s'agit du nombre de valeurs distinctes dans ce champ (équivalent d'une requête SELECT DISTINCT MonChamp ...). Dans le cas d'un index composé de plusieurs champs, cette valeur correspond au nombre de combinaisons distinctes des valeurs de ces champs au sein de la table (équivalent d'une requête SELECT DISTINCT MonChamp1,MonChamp2 ...).

4-5-2-2. Fields

Type : Collection

Cette collection regroupe l'ensemble des champs composant l'index. Chaque objet Field de cette collection doit posséder le même nom qu'un objet Field de la table concernée.

4-5-2-3. Foreign

Lecture seule.

Type : Boolean

Lorsque la propriété Foreign d'un index est égale à True, cela signifie que l'index est une clé étrangère. C'est notamment le cas pour les index ajoutés automatiquement par Access lorsque vous créez des relations entre les tables.

4-5-2-4. IgnoreNulls

Type : Boolean

Si vous définissez la propriété IgnoreNulls à True, les valeurs nulles ne seront pas indéxées. Cela peut être utile si vous prévoyer d'enregistrer un grand nombre de valeur nulles.

4-5-2-5. Name

Type : String

Cette propriété correspond au nom de l'index. Ce nom doit être unique dans la collection Indexes de l'objet TableDef correspondant.

Prévoyez des noms d'index aussi clairs que possible. Aussi certains préfixent le nom de leurs index par les initiales de leur rôle. Ainsi, une clé primaire sera notée PK_MonChamp (PK = Primary Key).

4-5-2-6. Properties

Type : Collection

Collection regroupant l'ensemble des objets Property de l'objet Index.

4-5-2-7. Required

Type : Boolean

Si la propriété Required est égale à True, cela signifie que les champs de l'index doivent tous recevoir une valeur, sans quoi, lors des mises à jour, une erreur sera levée.

4-5-2-8. Unique

Type : Boolean

Lorsque la propriété Unique est égale à True, les doublons sont interdits dans les champs de l'index.

Dans un index multi-colonnes, les valeurs A A et A B ne sont pas des doublons.

4-5-3. Liste des méthodes

Comme vous allez le remarquer, l'objet Index dispose de trés peu de méthodes. Qui plus est, ces méthodes ont déjà été traitées plus haut.

4-5-3-1. CreateField

Cette méthode est la même que celle appliquée aux objets TableDef, à la différence prés quelle ne propose qu'un seul paramètre : le nom du champ. Ce nom doit être identique à celui du champ de la table sur lequel repose l'index.

Syntaxe :

 
Sélectionnez
Set field = index.CreateField (name)
4-5-3-2. CreateProperty

Cette méthode n'est plus à présenter, et pour cause, nous la retrouvons à chaque chapitre de ce tutoriel. Aussi, je vous renvoie vers le chapitre des 4.2.5 Propriétés spécifiques.

4-5-4. Opérations sur les Index

Je vous propose deux petits exemples afin de mettre en pratique les notions abordées dans cette partie.

4-5-4-1. Créer un index

Soit une table Client possédant plusieurs champs dont NumeroClient. Nous allons créer une clé primaire sur ce champ.

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
Dim oFld As DAO.Field
Dim oInd As DAO.Index
'Instancie la base
Set oDb = CurrentDb
'Instancie la table
Set oTbl = oDb.TableDefs("Client")
'Crée l'index en le nommant PK_Client
Set oInd = oTbl.CreateIndex("PK_Client")
'Cree le champs dans l'index
Set oFld = oInd.CreateField("NumeroClient")
'Ajoute le champ à la collection Fields
oInd.Fields.Append oFld
'Définit la clé primaire
oInd.Primary = True
'Ajoute l'index à la table
oTbl.Indexes.Append oInd
'Rafraichit la collection
oTbl.Indexes.Refresh
4-5-4-2. Supprimer une clé primaire

Afin d'illustrer la suppression d'un index je vous propose de traiter le cas, où nous souhaitons supprimer la clé primaire de la table. Il nous faut donc parcourir l'ensemble des index de l'objet TableDef et les supprimer si leur propriété Primary est égale à True.

 
Sélectionnez
Private Sub SupprimerClePrimaire(oTbl As DAO.TableDef)
On Error GoTo err
Dim oInd As DAO.Index
'Parcours les index
For Each oInd In oTbl.Indexes
  'S'il s'agit d'une clé primaire, alors le supprimer
  If oInd.Primary Then
    oTbl.Indexes.Delete (oInd.Name)
  End If
Next oInd
Exit Sub
err:
MsgBox "Une erreur inattendue est survenue"
End Sub

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oTbl As DAO.TableDef
'Instancie la base
Set oDb = CurrentDb
'Instancie la table
Set oTbl = oDb.TableDefs("Client")
'Supprime la clé primaire
SupprimerClePrimaire oTbl

4-6. L'objet Relation

 

4-6-1. Définitions

Certaines applications sont jugées sur leur performance ou leur ergonomie. Lorsqu'il s'agit d'une base de données, les critères de sélection ne sont plus les mêmes. Le rôle principal d'un SGBD est de stocker des données et ce stockage doit être réalisé dans les meilleures conditions. Il serait dramatique que certaines données soient perdues fréquemment. Une application Access doit donc garantir la cohérence et l'intégrité des données. Imaginons une table Commande et une table Client, il serait illogique d'affecter une commande à un client non recensé dans la table Client. Afin d'éviter ce risque , le moteur Jet permet de créer des contraintes d'intégrités par le biais de ses objets Relations qui obligeront à saisir un client valide dans la table des commandes.

4-6-2. Liste des propriétés

4-6-2-1. Attributes

Type : Long

La propriété Attributes correspond au type de la contrainte représentée par l'objet Relation. La valeur de cette propriété doit être une (ou une combinaison) des constantes suivantes :

Constante Signification
dbRelationUnique Relation de type un à un.
dbRelationDontEnforce L'intégrité référentielle n'est pas appliquée.
dbRelationUpdateCascade Les mises à jours sont répercutées en cascade.
dbRelationDeleteCascade Les suppressions sont répercutées en cascade.
dbRelationLeft Les jointures seront de type Left par défaut.
dbRelationRight Les jointures seront de type Rigth par défaut.
4-6-2-2. Fields

Type : Collection

Cette collection regroupe l'ensemble des champs utilisés par la relation. Vous devez utiliser la méthode Relation.CreateField pour créer de nouveaux champs dans la relation et les ajouter à la collection Fields à l'aide de la méthode Append.

Chaque objet Field de la relation doit posséder le nom du champ concerné dans la table spécifiée dans la propriété Table. Ce champ aura comme propriété ForeignName, le nom d'un champ de la table spécifiée dans la propriété ForeignTable.

4-6-2-3. ForeignTable

Type : String

La propriété ForeignTable correspond au nom de la table externe de la relation. Ce nom doit être celui d'un objet TableDef présent dans la collection TableDefs de l'objet Database.

4-6-2-4. Name

Type : String

La propriété Name représente le nom de la relation. Il doit être unique au sein de la collection Database.Relations.

L'utilisation de nom précis facilitera les opérations de maintenance sur la base de données. Ainsi, une relation entre une table commande et une table client sera nommée Rlt_CommandeClient. Evitez d'appeler vos objets Relation1, Table1, Requete1...

4-6-2-6. Properties

Type : Collection

La collection Properties regroupe l'ensemble des propriétés de l'objet Relation.

4-6-2-7. Table

Type : String

La propriété Table correspond au nom de la table principale sur laquelle porte la relation. Il s'agit de la table possédant la clé primaire utilisée dans la relation. A contrario, la propriété ForeignName correspond à la table possédant la clé étrangère de la relation.

4-6-3. Liste des méthodes

Les objets de type Relation ne possède qu'une seule méthode : CreateField. Celle ci permet de créer un champ à ajouter à la relation.

Syntaxe :

 
Sélectionnez
Set field = relation.CreateField (name)

Name correspond au nom du champ de la clé primaire utilisée dans la relation.

4-6-4. Exemple de définition des propriétés

Considérons deux tables et la relation suivante :

Exemple de relation

Paramètres de la relation :

Image non disponible

Les propriétés d'une telle relation seront définies ainsi :

  • Attributes : dbRelationUpdateCascade
  • Fields : Un objet Field nommé "NumClient" et ayant comme propriété ForeignName : "IDClient"
  • ForeignTable : "Commande"
  • Name : "Rlt_CommandeClient"
  • PartialReplica : False (Non utilisé)
  • Table : "Client"

4-6-5. Opérations sur les relations

Je vous propose une suite de quelques exemples de code VBA afin de manipuler les objets Relations

4-6-5-1. Créer une relation

Reprenons l'exemple ci-dessus. Je vous livre le code correspondant à une telle relation.

La première méthode consiste à créer la relation et affecter à chacune des propriétés sa valeur correspondante.

 
Sélectionnez
Dim oDb As DAO.Database
Dim oRlt As DAO.Relation
Dim oFld As DAO.Field
Set oDb = CurrentDb
'Crée la relation
Set oRlt = oDb.CreateRelation
'Définit les propriétés
oRlt.Attributes = dbRelationUpdateCascade
oRlt.ForeignTable = "Commande"
oRlt.Name = "Rlt_CommandeClient"
oRlt.Table = "Client"
'Crée le champ dans la relation
Set oFld = oRlt.CreateField("NumClient")
'Définit le nom de la clé externe
oFld.ForeignName = "IDClient"
'Ajoute le champ
oRlt.Fields.Append oFld
'Rafraîhit la collection Relations
oDb.Relations.Refresh
'Ajoute la relation
oDb.Relations.Append oRlt
'Rafraîhit la collection Relations
oDb.Relations.Refresh
'Ferme la base de données
oDb.Close
Set oDb = Nothing

La seconde méthode consite à utiliser l'ensemble des arguments de la méthode CreateProperty. En effet celle-ci permet d'initialiser le nom de la relation, de la table, de la table externe ainsi que les attributs.

Syntaxe :

 
Sélectionnez
Set relation = database.CreateRelation (name, table, foreigntable, attributes)

Notre code devient alors :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oRlt As DAO.Relation
Dim oFld As DAO.Field
Set oDb = CurrentDb
'Crée la relation
Set oRlt = oDb.CreateRelation("Rlt_CommandeClient", "Client", _
  "Commande", dbRelationUpdateCascade)
'Crée le champ
Set oFld = oRlt.CreateField("NumClient")
'Définit le nom de la clé externe
oFld.ForeignName = "IDClient"
'Ajoute le champ
oRlt.Fields.Append oFld
'Rafraîhit la collection Relations
oDb.Relations.Refresh
'Ajoute la relation
oDb.Relations.Append oRlt
'Rafraîhit la collection Relations
oDb.Relations.Refresh
'Ferme la base de données
oDb.Close
Set oDb = Nothing
4-6-5-2. Compter les relations utilisant un champ donné

Cette fonction a déjà été utilisé lors de l'opération qui consistait à supprimer un champ dans un objet TableDef.

Le principe est simple, un champ est utilisé par une relation si :

  • La table dont il provient est présent dans la propriété Table de la relation et que le nom d'un champ de la relation est égal au nom de ce champ.
  • La table dont il provient est présent dans la propriété ForeignTable de la relation et qu'au moins un champ de cette relation possède une propriété ForeignName égale au nom de ce champ.

Il nous suffit donc de boucler sur l'ensemble des objets Relation de la base de données.

 
Sélectionnez
Private Function ChampDansRelation(oBaseDeDonnees As DAO.Database, _
 strNomTable As String, strNomChamp As String) As Integer
On Error GoTo err
 Dim bolUtilise As Boolean
 Dim oRlt As DAO.Relation
 Dim oFld As DAO.Field
  'Parcours les relations
  For Each oRlt In oBaseDeDonnees.Relations
   bolUtilise = False
   If oRlt.Table = strNomTable Then
     'Pour chaque champ, contrôle le nom du champ
     For Each oFld In oRlt.Fields
       If oFld.Name = strNomChamp Then bolUtilise = True
     Next oFld
  ElseIf oRlt.ForeignTable = strNomTable Then
    'pour chaque champ, contrôle le nom étranger
    For Each oFld In oRlt.Fields
       If oFld.ForeignName = strNomChamp Then bolUtilise = True
    Next oFld
  End If
  'Si la relation utilise le champ, on incremente le resultat
  If bolUtilise Then ChampDansRelation = ChampDansRelation + 1
 Next oRlt
Exit Function
err:
ChampDansRelation = -1
End Function

Il devient alors possible de connaître le nombre de relations utilisant le champ NumClient de la table Client.

Utilisation :

 
Sélectionnez
Dim oDb as DAO.Database
Set oDb=CurrentDB
MsgBox ChampDansRelation(oDb, "Client", "NumClient")
4-6-5-3. Supprimer les relations autour d'une table

Il peut s'avérer par fois utile de supprimer les relations portant sur une table avant de pouvoir modifier la structure de la table. Cette opération peut s'avérer fastidieuse et je vous suggère de créer une fonction automatisant cette tâche.

 
Sélectionnez
Public Function SupprimerRelationTable(oBaseDeDonnees As _
 DAO.Database, strNomTable As String) As Integer
Dim oRlt As DAO.Relation
'Pour chaque relation,
For Each oRlt In oBaseDeDonnees.Relations
  'si la table est utilisée
 If oRlt.Table = strNomTable Or oRlt.ForeignTable = strNomTable Then
   'Supprime la relation
   oBaseDeDonnees.Relations.Delete oRlt.Name
   oBaseDeDonnees.Relations.Refresh
   'Incrémente le compteur
   SupprimerRelationTable = SupprimerRelationTable + 1
 End If
Next oRlt
End Function

La fonction SupprimerRelationTable retourne le nombre de relations supprimées.

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oRlt As DAO.Relation
Set oDb = CurrentDb
'Supprime les relations de la table Client
MsgBox SupprimerRelationTable(oDb, "Client")

4-7. L'objet QueryDef

Nous sommes sur le point d'aborder le dernier objet de la partie DDL du modèle DAO. Cette partie commencera par une rapide présentation de l'objet, suivie de la liste des propriétés et des méthodes puis enfin, quelques exemples d'utilisation en VBA.

4-7-1. Définitions

Access est un des rares SGBDR permettant de stocker des requêtes SQL dans la base de données sans qu'il ne s'agissent de vues. Certes, une vue est le résultat de cette requête mais son contenu est statique. Pour visualiser les modifications intervenues dans le jeu d'enregistrements entre deux instants, il est nécessaire de recréer la vue. Une requête stockée dans une base de données Access est dynamique. Elle est réexecutée à chaque fois que vous la réouvrez. De telles requêtes sont modélisées par l'objet QueryDef dans DAO.

Ces requêtes peuvent être de plusieurs types : sélection, union, insertion, mise à jour, suppression, analyse croisée ...

Requêtes paramétrées :

DAO propose de gérer les paramètres des requêtes via la collection Parameters de l'objet QueryDef. Ces requêtes sont trop souvent oubliées dans Microsoft Access et remplacées par des requêtes SQL codées directement dans VBA. Pourtant, la manipulation de ces requêtes est trés simple :

 
Sélectionnez
PARAMETERS Param_Num Text ( 10 );
SELECT *
FROM Client
WHERE NumClient=Param_Num;

La clause PARAMETERS liste l'ensemble des paramètres avec leur type et (optionnellement) leur taille. La seconde partie de la requête, est celle que vous auriez écrit sans paramètre.

Ainsi, pour ouvrir un Recordset sur le client possédant le numéro AF36, le code VBA serait :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oQdf As DAO.QueryDef
Dim oRst As DAO.Recordset
Set oDb = CurrentDb
Set oQdf = oDb.QueryDefs("R01")
oQdf.Parameters("Num").Value = "AF36"
Set oRst = oQdf.OpenRecordset

Afin de comparer, voici le même code sans utiliser les requêtes paramétrées :

 
Sélectionnez
Dim oDb As DAO.Database
Dim oSQL As String
Dim oRst As DAO.Recordset
Dim StrSQL As String
Set oDb = CurrentDb
StrSQL = "SELECT * FROM Client " & _
         "WHERE NumClient=" & Chr(34) & "AF36" & Chr(34)
Set oRst = oDb.OpenRecordset(StrSQL)

L'utilisation des requêtes paramétrées apparait donc plus intuitive et évite de manipuler des fonctions de conversion de chaîne. De plus, si demain, nous souhaitons modifier notre requête pour y ajouter une jointure, il nous suffit de modifier le code SQL correspondant dans la requête paramétrée. Si au contraire nous n'utilisons pas ces requêtes, il nous faut alors modifier notre code VBA aux différents endroit où la "requête" est mentionnée.

Si vous ouvrez un objet Database Db2 depuis la base de données Db1 et que vous éxecutez un objet QueryDef de Db2, les données seront prélevées depuis Db2 et non dans Db1. Chaque objet QueryDef travaille sur les données de l'objet Database qui le contient.

4-7-2. Liste des propriétés

Vous trouverez ci-dessous la liste des propriétés disponibles pour les objets QueryDef.

La propriété Prepare n'est pas documentée dans ce tutoriel.

4-7-2-1. Connect

Lecture seule.

Type : String

La propriété Connect renvoie la chaine de connexion utilisée par la requête lorsque celle ci provient d'une autre base de données.

4-7-2-2. DateCreated

Lecture seule.

Type : Date

Cette propriété renvoie la date à laquelle a été créé la requête.

4-7-2-3. Fields

Lecture seule.

Type : Collection

Une nouvelle fois, nous rencontrons cette collection qui regroupe l'ensemble des champs retournés par la requête.

Notez que vous pouvez différencier le nom d'un champ et son alias en utilisant la propriété SourceField de l'objet Field.

4-7-2-4. LastUpdated

Lecture seule.

Type : Date

Cette propriété renvoie la date à laquelle a été modifiée la requête pour la dernière fois.

4-7-2-5. MaxRecords

Type : Long

La propriété MaxRecords correspond au nombre d'enregistrements à renvoyer au maximum. Cette propriété peut être utilisée lorsque le poste client ne dispose pas de ressources suffisantes afin de traiter l'ensemble des données.

Cette propriété est disponible uniquement pour les requêtes utilisant une source de données ODBC.

4-7-2-6. Name

Type : String

La propriété Name représente le nom de la requête. Il doit être unique au sein de la collection Database.QueryDefs.

Lorsque vous utilisez la méthode CreateQueryDef et que vous ne spécifiez pas l'argument Name, la requête n'est pas sauvegardée dans la base de données à la fin du traitement.

4-7-2-7. Parameters

Type : Collection

Cette collection regroupe l'ensemble des paramètres d'un objet QueryDef. Il est impossible d'ajouter ou de supprimer des éléments de cette collection.

Un exemple d'utilisation de requête paramétrée est disponible dans les définitions des QueryDefs ci-dessus.

4-7-2-8. Properties

Type : Collection

La collection Properties regroupe l'ensemble des propriétés de l'objet QueryDef.

4-7-2-9. RecordsAffected

Lecture seule.

Type : Long

Dans le cas d'une requête action (Insert,Update,Delete), la propriété RecordsAffected retourne le nombre d'enregistrements affectés par l'ordre SQL.

4-7-2-10. ReturnsRecords

Type : Boolean

Cette propriété définit ou renvoie une valeur indiquant si une requête retourne des enregistrements.

La propriété ReturnsRecords est disponible uniquement pour les requêtes SQL directes.

4-7-2-11. SQL

Type : String

La propriété SQL correspond au code SQL de la requête.

Cette propriété correspond à l'argument SQL de la méthode Database.CreateQueryDef.

4-7-2-12. Type

Type : Long

La propriété Type renvoie le type de la requête. Cette propriété est définie automatiquement lorsque vous créez l'objet QueryDef. Toutefois, elle peut vous permettre de recenser les objets QueryDef suivant leur rôle.

Les valeurs possibles sont :

Constante Signification
dbQAction Action
dbQAppend Ajout
dbQCompound Non documentée
dbQCrosstab Analyse croisée
dbQDDL Définition de données
dbQDelete Suppression
dbQMakeTable CreateTable
dbQProcedure Procédure stockée
dbQSelect Sélection
dbQSetOperation Union
dbQSPTBulk Requête ne renvoyant pas d'enregistrement
dbQSQLPassThrough Requête SQL Directe
dbQUpdate Mise à jour
4-7-2-13. Updatable

Lecture seule.

Type : Boolean

Cette propriété renvoie True si les données de la requête ne peuvent être modifiées.

4-7-3. Liste des méthodes

4-7-3-1. CreateProperty

La méthode CreateProperty crée un nouvel objet Property. Plus de détails sont disponibles dans le chapitre 4.2.5 Propriétés spécifiques.

4-7-3-2. Execute

La méthode Execute éxecute une requête Action. Une erreur sera levée si vous tentez d'exécuter une requête de sélection.

Syntaxe :

 
Sélectionnez
querydef.Execute options

L'argument options doit être une (ou une combinaison) des constantes suivantes :

Constante Signification
dbDenyWrite Interdit l'accés en écriture pour les autres utilisateurs
dbInconsistent (Valeur par défaut) Exécute des mises à jour sans vérifier la cohérence (intégrité réferentielle)
dbConsistent Exécute des mises à jour en vérifiant la cohérence
dbSQLPassThrough Exécute une requête SQL Direct
dbFailOnError En cas d'erreur, les mises à jour sont annulées
dbSeeChanges Génère une erreur d'exécution si un autre utilisateur modifie les données que vous êtes en train de modifier

Vous pouvez utiliser la propriété RecordsAffected pour connaître le nombre d'enregistrements modifiés.

4-7-3-3. OpenRecordset

La méthode OpenRecordset ouvre un jeu d'enregistrements sur le résultat de la requête.

Syntaxe :

 
Sélectionnez
Set recordset = object.OpenRecordset (type, options, lockedits)

Cette méthode sera largement abordée dans le chapitre suivant.

4-7-4. Opérations sur les requêtes

4-7-4-1. Création d'une requête

Dans notre exemple nous allons créer une requête de sélection complète de la table clients. Il s'agit d'une requête simple et la méthode à appliquer est la même pour tout type de requête.

 
Sélectionnez
Sub CreerRequete()
On Error GoTo Err
Dim oDb As DAO.Database
Dim strCodeSql As String
'Instancie l'objet database
Set oDb = CurrentDb
'Stocke le code SQL dans une variable
strCodeSql = "SELECT * FROM Client"
'Crée la requête
oDb.CreateQueryDef "R01_SelectionClient", strCodeSql

Finally:
oDb.Close
Set oDb = Nothing

Err:
'Gére les erreurs
Select Case Err.Number
  Case 3012: MsgBox "La requête existe déjà"
  Case Else: MsgBox "Une erreur inconue est survenue"
End Select
Resume Finally
End Sub
4-7-4-2. Modifier le code d'une requête

Afin de modifier le code SQL, il faut procéder en deux étapes :

  • Récuperer l'objet QueryDef correspondant (S'il existe).
  • Modifier la propriété SQL de l'objet.
 
Sélectionnez
Public Function ModifierSQL(oBaseDeDonnees As DAO.Database, _
  strNomRequete As String, strCodeSQL As String) As Boolean
On Error GoTo Err
Dim oQdf As DAO.QueryDef
'Instancie l'objet QueryDef
Set oQdf = oBaseDeDonnees.QueryDefs(strNomRequete)
'Modifie le code SQL
oQdf.SQL = strCodeSQL
'Renvoie True
ModifierSQL = True
Exit Function
Err:
'Gere les erreurs
Select Case Err.Number
  Case 3265: MsgBox "Impossible de trouver la requête à modifier"
  Case Else: MsgBox "Une erreur inattendue est survenue"
End Select
End Function

Utilisation :

 
Sélectionnez
Dim oDb As DAO.Database
Set oDb = CurrentDb
If ModifierSQL(oDb, "R01_SelectionClient", "SELECT NumClient FROM Client") Then
 MsgBox "Le code SQL de la requête a été modifié avec succés"
End If
4-7-4-3. Lister les requêtes

La propriété Type retourne le type d'un objet QueryDef. Il est donc possible de lister l'ensemble des requêtes de sélection.

 
Sélectionnez
Dim oDb As DAO.Database
Dim oQdf As DAO.QueryDef
'Instancie l'objet Database
Set oDb = CurrentDb
'Boucle sur les objets QueryDef
For Each oQdf In oDb.QueryDefs
  'S'il s'agit d'une requête de sélection, alors afficher son nom
  If oQdf.Type = dbQSelect Then MsgBox oQdf.Name, , "Sélection"
Next oQdf

précédentsommairesuivant

Copyright © 2005 WARIN Christophe. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.