VBA : Opérateur Like commun à SQL, VBA et aux Expressions

L’opérateur logique LIKE est un opérateur d’égalité strictement réservé aux chaines de type texte. Sa particularité est de pouvoir comparer des valeurs à un masque composé de caractères de substitution.

Glossaire

Dans cet article : Masque, caractères de substitution, masque de comparaison, règles désigne la deuxième partie du Like.

Comme le = mais plus flexible

Il s’utilise comme n’importe quel opérateur logique soit :

[Expression à comparer] Like [masque de comparaison]

Retourne un True, False ou Null.

'En VBA
If ExpressionAcomparer Like ExpressionLike then

'Expression
=iif(ExpressionAcomparer Like ExpressionLike;True;False)

'SQL
... WHERE ExpressionAcomparer Like ExpressionLike ...

La syntaxe est identique en SQL (*), VBA ou dans les expressions.

(*) Les moteurs de base de données utilisent plutôt les jockers ANSI.
* devient % et ? devient _.

Les jokers

Les jokers sont des caractères de substitution permettant de composer le masque de comparaison. Ils sont combinables.

L’astérisque *

L’astérisque * remplace plusieurs caractères alphanumériques et spéciaux.

ExpressionDescription
« abcde45é# » like « abc* »Toute chaine commençant par abc
« 01è+abc » like « *abc »Toute chaine finissant par abc
« 01abc_ç » like « *abc* »Toute chaine contenant abc
« ab(6ç1c » like « ab*c »Toute chaine commençant pas ab et finissant par c

    Le point d’interrogation ?

    Le point d’interrogation ? remplace un seul caractère quel qu’il soit : lettre, chiffre, caractère spécial.

    ExpressionDescription
    « A » Like « ? »Toute chaine contenant 1 caractère
    « abcD » like « abc? »Toute chaine commençant par abc avec 1 caractère à la fin.
    « 0adc » like « ?abc »Toute chaine commençant par 1 caractère et finissant par abc.
    « 0abc) » like « ?abc? »Toute chaine contenant abc avec 1 caractère de part et d’autre.
    « x4z » like « ??? » Toute chaine contenant 3 caractères contigus.

      Comme le point d’interrogation remplace 1 caractère on peut en utiliser plusieurs à la suite dans un masque de comparaison.

      Le dièse #

      Le caractère dièse # fonctionne comme le point d’interrogation mais remplace un chiffre, donc ni lettre, ni un caractère spécial.

      ExpressionDescription
      « 1 » like « # »un chiffre de 0 à 9
      identique à [0-9]
      « 100.99 » like « ###.## »3 chiffres et 1 point et 2 chiffres
      « +100.99 » like « [!-]###.## » »
      « A25X17 » like « [A-C]##[!W]## »A, B ou C, 2 chiffres, pas de W, 2 chiffres

      Les crochets [ ]

      Les crochets permettent de remplacer un caractère situé dans une plage particulière. Il a aussi comme fonction de pouvoir utiliser les caractères de substitutions en tant que caractères numériques.

      ExpressionDescription
      « B » Like « [A -C] » Le tiret détermine une plage de caractères contigus entre 2 bornes. Ici A, B ou C.
      « C » Like « [A,C,E] »La virgule, détermine une série de caractères. Ici A, C ou E.
      « ANZ » Like « A[M-O][W,Z] »La combinaison des 2. Ici A, M ou N ou O et W ou Z.
      « B2 » Like « [A-C][!1] »A, B, C mais pas 1.
      « Cbc3 » Like « [A-D]bc[!0] »A à D puis bc puis <>0
      « abc2 » vrai !
      « add2 » faux ! (pas de bc)
      « abc0 » faux ! (0 à la fin)
      « rL5 » Like « [a-z][A-Z][0-9] » Uniquement en VBA avec un Option Compare Binary !
      La réponse doit contenir 3 digits. 1 lettre en minuscule, 1 en majuscule, un chiffre.

      « aB1 » vrai !
      « AX9 » faux ! A est en majuscule.
      « aAd » faux ! pas de chiffre à la fin.
      « aB123 » faux ! 2 et 3 en trop.
      « abR5 » faux ! a ou b en trop.
      « BMW10 » Like « [A-C]M[S-Z]10 »A,B ou C et M et X,S à Z et 10
      « ‘bonjour' » Like « [«  »,’]*[«  »,’] »On teste si la chaine est entourée de  » ou ‘. (*)

      (*) Ceci n’est que pour l’exemple, cette comparaison n’est pas du tout robuste.

      Crochets avec des caractères spéciaux

      Avec des caractères spéciaux la musique est la même. Seules les paroles changent.

      ExpressionDescription
      « Ä » like « [À-Æ] »du 0192 au 0198
      « ) » like « [!-/] »du 0033 au 0047

      Ce qui ne fonctionne pas avec les crochets

      Les combinaisons ne fonctionnent pas. Il faut obligatoirement séparer vos règles. Ici quelques exemples.

      Expression FAUSSEDescriptionExpression VRAI
      « [Z-A] »Toujours respecter l’ordre de tri. « [A-Z] »
      « [8-5] »Toujours respecter l’ordre de tri.« [5-8] »
      « [A-C,!1] »Pas d’assemblage« [A-C][!1] »
      « [a-z,A-Z,0-9] »Pas d’assemblage« [a-z][A-Z][0-9] »
      « [a-z!] »La négation toujours au début« [!a-z]

      Attention ! Les plages de caractères doivent toujours être placées dans l’ordre croissant. Par exemple [A-C] est valide alors que [C-A] ne l’est pas, de même que [0-9] est valide et [9-0] ne l’est pas. Pour déterminer l’ordre des caractères, consultez la table ASCII et réalisez des tests.

      La négation

      On peut utiliser 2 types de négation avec l’opérateur Like.

      Le Not habituel qui englobe l’ensemble de la comparaison, comme dans l’exemple suivant

      ExpressionDescription
      Not « abc » Like « a?c »Ne correspond pas
      Not « abc » Like « a*c »Ne correspond pas

      Le ! point d’exclamation dans les crochets du masque de comparaison. Dans ce cas on obtient plus de finesse dans la comparaison.

      ExpressionDescription
      « abc » Like « [!a]bc »Faux ! le premier caractère ne doit pas être « a ».
      « 1bc » Like « [!a-z]bc »Vrai ! le premier caractère n’est pas un caractère compris entre a et z.

      Rechercher les caractères génériques

      On peut être emmené à rechercher l’un des caractères de substitution dans une chaîne. Quelques exemples dans le tableau suivant.

      ExpressionDescription
      « Es-tu là ? » Like « Es-tu là [?] »Le point d’interrogation à la fin d’une phrase.
      « Es-tu là ! » Like « Es-tu là ? »Un caractère à la fin. ? ou autre.
      « 45 » like « []## »Pas d’espace en début de chaine.

      « AB » Like « A[]B »
      Pas d’espace entre A et B
      « A B » est faux !
      « [Vrai] » Like « [[]Vrai] »Commence par le crochet ouvrant [.
      Le crochet fermant ] n’a pas besoin d’être isolé.
      « #N/A » Like « [#]N/A »Une chaîne commençant par le caractère #.
      «  » »abc » » » Like «  » »??? » » »Une chaine commençant par  » et finissant par « .
      « AOC « Médoc » 33″ like « AOC «  »* » » ## »AOC « une chaine » 33
      «  » »abc » » » Like chr(34) & « abc » & chr(34)On peut aussi utiliser ce genre de notation où on utilise chr(34) pour retourner le « .

      Avec d’autres types de données

      Le LIKE peut fonctionner avec d’autres types de données mais les valeurs testées seront transformées en texte pour leur évaluation.

      Par exemple avec des dates on peut rechercher des séries.

      ExpressionDescriptionEquivalent VBA
      date() Like « ##/##/2032 »nous somme en 2032Year(date)=2009
      date() Like « ##/1#/2032 »Nous sommes au 4e trimestre 2032Format(Date, »q »)=4 (*)
      date() Like « 10/##/#### »Nous somme le 10Day(date)=10

      (*) Dans les expressions la virgule doit être remplacée par un point virgule.
      Format(Date; »q »)

      On peut trouver certains intérêts à utiliser un LIKE plutôt qu’une expression plus académique, mais attention aux résultats qui peuvent être surprenants et loin de vos attentes. Un Like n’est pas forcément plus performant par rapport aux fonctions prévues.

      (*) t en SQL, q en VBA. On peut également utiliser la fonction DatePart().

      Quelques tests dans la fenêtre d’exécution de VBE

      Vous pouvez vous entrainer dans la fenêtre d’exécution pour concevoir vos masques.

      ? "ceci#est [la preuve] par 9 ?" LIKE "ceci[#]*[[]la preuve[]]*[0,9]?[?]"
      Vrai
      

      La casse

      Vous avez pu remarquer que Like n’est pas sensible à la casse lors de la comparaison.
      Il y a cependant un moyen d’en tenir compte. Pour cela il faut modifier le paramètre Option Compare situé dans l’en-tête du module VBA qui contient l’instruction.

      Option Compare Database
      Option Explicit
      OptionDescription
      Option Compare DatabaseNe tient pas compte de la casse
      « AAA » est égal à « aaa »
      Option Compare BinaryTient compte de la casse
      « AAA » est différent de « aaa »

      Pour une prise en charge de la casse dans vos requêtes vous avez d’autres solutions :

      • Changer de moteur de base de données. Ms Sql server, Mariadb, PostgreSQL, Oracle prennent en charge la casse.
      • Créer votre propre fonction VBA que vous appellerez depuis vos requêtes. Voir l’exemple ci-dessous.

      Mettez cette fonction dans un module Standard.

      Option Compare Binary  'respect de la casse
      Option Explicit
      
      Public Function isLike(value As Variant, masque As Variant) As Variant
      'Fabrice Constans (C2ED)
      'Tient compte d'un passage de Null et de son retour.
      'Appel de la fonction dans tous les composants d'Access
      'VBA : If isLike(date() ,"##/##/2032") then
      'Expression : =Iif(isLike(date() ;"##/##/2032");"Vrai";"False")
      'SQL : SELECT madate FROM matable WHERE isLike(madate ,"##/##/2032");
      
          isLike = value Like masque
      
      End Function
      
      

      Attention à la valeur Null !

      Le Null est retourné lorsque au moins l’une des expressions vaut Null.

      ExpressionDescription
      Null Like « * »La valeur à comparer vaut Null
      « abc » Like NullLe masque de comparaison vaut Null
      Null Like NullLes 2 côté de la comparaison valent Null
      ? Null Like "*"
      Null
      ? "giraphe sophie" Like Null
      Null
      ? Null Like null
      Null

      Conclusion

      En conclusion voici un opérateur bien utile qui rempli 90% des demandes en toutes circonstances. Il pêche cependant par le manque de prise en compte de la casse avec le moteur de base de données interne d’Access, ACE et dans les expressions des formulaires, état et macros. Ceci peut être rapidement contourné avec la petite fonction isLike() indiquée dans cet article.
      Pour des besoins plus poussés n’hésitez pas à utiliser RegEx. Si le moteur ACE ne le prend pas en charge la plupart des autres moteurs de base de données ont intégré RegEx dans leur SQL.

      VBA : Replace VBA boosté au RegEx

      La fonction Replace() de VBA est largement employée par les développeurs. Mais lorsqu’ils souhaitent faire une recherche moins statique, ils regardent avec envie l’opérateur Like sans pouvoir faire un mix des 2.

      Etant confronté à des traitements de chaines de caractères, je me suis créé une petite fonction dérivée du Replace, avec une touche de VBScript.RegEx.

      Voici ce que cela donne :

      Function ReplaceReg(ByVal expression As String, ByVal findPattern As String, ByVal replace As String, _
                          Optional ByVal caseSensitive As Boolean = False) As String
      '---------------------------------------------------------------------------------------
      ' Procedure : ReplaceReg
      ' Author    : Fabrice CONSTANS (C²ED - c2ed.software@gmail.com)
      ' Date      : 24/09/2024
      ' Purpose   : Un Replace() VBA avec du RegEx
      ' Parameters: expression à analyser, findPattern pattern regex, replace ce qui doit être
      '             mis à la place du pattern, caseSensitive tenir compte de la casse. 
      ' Copyright : © L'intégralité du code ci-dessous est la propriété de son auteur (Author)
      '             Son utilisation est strictement limitée au présent logiciel.
      ' Pattern regex : https://documentation.help/MS-VBScript-fr/vsproPattern.htm
      ' Exemple de patterns :
      'LIKE      RegEx
      'chaton*   ^chaton.
      'chat*n    chat.n
      'chat[A,E] chat[AE]
      'chat=,    chat=.,
      'chat      chat$
      '---------------------------------------------------------------------------------------
      
          On Error GoTo Errsub
          Dim regEx As Object
          Set regEx = CreateObject(VBScript.RegExp)
          
          With regEx
              .pattern = pattern
              .Global = True
              .IgnoreCase = Not caseSensitive
          End With
          
          ReplaceReg = regEx.replace(expression, replace)
          Exit Function
      
      Errsub
          ReplaceReg = inputText ' retourne le texte original en cas d’erreur
          
      End Function

      Le contrat d’utilisation

      • expression : texte à modifier.
      • findPattern : pattern RegEx conforme au standard VBScript.Regex
      • replace : texte de remplacement
      • caseSensitive indique si la casse doit être prise en compte.

      Les patterns

      Les expressions régulières RegEx sont bien plus puissant que le l’opérateur Like, mais plus complexe à maitriser.
      En revanche, l’opérateur Like est disponible à la fois SQL et en VBA, où il fonctionne de la même manière.

      Remplacement global

      Comme le Replace dans son utilisation le plus basique, notre fonction remplace toutes les occurrences rencontrées. C’est la propriété Globle=True qui garantie se comportement. Si on utilise False, seule la première occurrence sera traitée.

      Perfectible ? Toujours !

      La fonction est perfectible : on pourrait y ajouter des options comme start ou count pour limiter les remplacements. Mais dans la majorité des cas, elle fait le job.

      • start qui exclue les X premiers caractère de l’expression
      • count qui opère X remplacement

      Je n’utilise jamais le Start, par contre le Count peut s’avérer utile dans certains cas.

      Exemple d’utilisation

      ? replaceReg("ODBC;DRIVER={MySQL ODBC 8.0 UNICODE Driver};SERVER=192.168.10.1;DATABASE=baseLocale;Option=43;charset=UTF8;",";SERVER=.*?;",";SERVER=azure.microsoft.com;")
      ODBC;DRIVER={MySQL ODBC 8.0 UNICODE Driver};SERVER=azure.microsoft.com;DATABASE=baseLocale;Option=43;charset=UTF8;

      On a remplacé n’importe quelle adresse du server par ;SERVER=azure.microsoft.com;

      Conclusion

      Si vous traitez des chaînes complexes en VBA, pensez RegEx. Et si vous aimez cette fonction, n’hésitez pas à l’utiliser — en conservant l’en-tête bien sûr.

      ACCESS : Qui se souvient de Money ?

      J’ai utilisé pendant des années Money, je le trouvais pratique et sur Windows il n’y avait pas beaucoup de choix. Les produits sympa mais gratuit ont souvent une durée de vie limitée. Microsoft a arrêté sa distribution en 95 ou 97. Beaucoup d’utilisateurs furent déçus par cette décision.

      Argentek ou le retour d’un Money-Like

      Quand un produit disparait on est tenté d’en choisir un autre… ou d’en concevoir un tel qu’on le souhaite. C’est ce que j’ai fait avec Argentek. J’ai pris ce bon vieil Access 2000 et j’ai créé un logiciel de comptes bancaires. Aujourd’hui nous sommes à la version 3.13.06 qui fonctionne avec Access 2016-2024. Au début le code était ouvert ce qui a permis aux utilisateurs de créer leur propre fork. Malheureusement, quelques utilisateurs ont tenté de s’approprier le produit à des fins mercantiles si bien qu’aujourd’hui le projet est livré compilé.

      Les atouts d’Argentek

      Il fonctionne avec Access 2016-2024, (version antérieure disponible sous condition) c’est un produit simple et robuste basé sur ACE et intégralement écrit en VBA. Cela fait de lui un produit autonome qui peut être utilisé avec une licence complète ou un runtime.
      Ses fonctionnalités sont les suivantes :

      • Multicompte
      • Transfert de compte à compte
      • Opérations automatisées (Unique, Mensuelle, Trimestrielle, Annuelle, Personnalisée)
      • Gestion des tiers
      • Gestion des catégories
      • Solde par opération (indique pour chaque ligne d’opération le solde du compte)
      • Colonne Débit et Crédit
      • Pointage d’opérations
      • Sauvegarde automatisée (dernière sauvegarde effectuée)
      • Tri et Filtre sur les Opérations

      Liste des paramètres possibles :

      • Activer/Désactiver la sauvegarde automatisée
      • Confirmation ou non de la sauvegarde
      • Suppression des opérations automatisées Unique
      • Protection contre la modification des opérations pointées
      • La dernière opération apparait en haut de la liste
      • Déclenchement de la sauvegarde à la fermeture
      • Chemin pour la sauvegarde
      • Fichier de sauvegarde zippé
      • Chemin du fichier de données
      • Compte affiché par défaut

      Les retours d’utilisateurs

      Les utilisateurs ont apprécié sa gratuité, sa simplicité de prise en main et sa fiabilité. J’ai reçu beaucoup de messages d’encouragement, de remerciement et de demandes d’évolution. Le plus étonnant message est celui d’un gradé de l’armée d’un pays africain qui affirmait gérer les comptes de l’armée… Pourquoi pas ?

      Vous êtes intéressé par Argentek ?
      Contactez moi !

      VBA : Classe et gain de temps

      Le concept de classe en VBA a quelques lacunes mais il ne doit pas être négligés pour autant.

      En effet, grâce à elles on peut gagner un temps précieux. Voici un petit exemple fonctionnel d’une classe qui gère les évènements DblClick et NotInList d’une zone de liste déroulante. Le DblClick permet d’ouvrir un formulaire de saisie/édition avec l’item de la liste, tandis que NotInList permet de saisir l’item.

      Créer le module de classe

      Dans VBE faites Insertion/Module de classe. En bas à gauche dans la fenêtre des propriétés vous devez renseigner le nom de l’instance : cComboBox

      Saisissir

      Saisissez la première ligne de code suivante immédiatement après les lignes Option.

      Private WithEvents LmCombo          As Access.ComboBox

      WithEvents indique que cet objet pourra lever des évènements.

      Access.ComboBox est le type Liste déroulante.

      Ensuite nous aurons besoin de variables de type String pour stocker différents éléments nécessaires à l’ouverture du formulaire.

      'nom du formulaire qui est appelé lors du notinlist
      Private strFormNameLie     As String       
         'nom du champ id de la table recevant la nouvelle valeur
      Private strIdFieldName     As String       
         'nom du champ recevant la valeur saisie dans la combo 
      Private strControlName     As String       
         'nom de la table recevant la nouvelle valeur
      Private strTableFormLie    As String

      En suivant nous écrivons deux méthodes habituelles dans les classes.

      La Class_Initialize est exécutée au démarrage de la classe. Elle ne contient rien dans cet exemple mais si vous devez initialiser une variable ou une propriété c’est ici que vous devrez le faire.

      Private Sub Class_Initialize()
      ' Initialise
      
      End Sub

      Tandis que Class_Terminate est exécuté lorsqu’on libère la classe, qu’on la décharge. Celle-ci est importante car c’est là qu’on décharge également toutes nos variables.

      Private Sub Class_Terminate()
      ' libère les variables
          On Error Resume Next
          Set LmCombo = Nothing
      End Sub

      Pour charger la combo depuis le formulaire il faut utiliser un « Setteur ». Le voici !

      Public Property Set objComboBox(objCombo As Access.ComboBox)
          Set LmCombo = objCombo
          LmCombo.OnDblClick = "[Event Procedure]"
          LmCombo.OnNotInList = "[Event Procedure]"
      End Property

      On voit qu’on lui passe un objet combobox et qu’il est chargé dans la variable définie précédemment. Les 2 lignes suivantes permettent d’activer l’écoute des évènements dont nous avons besoin : DblClick et NotInList.

      Les autres variables ou propriétés de la classe doivent également être valorisées toujours à l’aide de Setteurs.

      Public Property Let frmNameLie(strNomFormulaireLie As String) 'le formulaire de saisie modif à ouvrir
          strFormNameLie = strNomFormulaireLie
      End Property
      
      Public Property Let idFieldName(strNomIdFieldFormulaireLie As String) 'le nom de la colonne id de la source du formulaire
          strIdFieldName = strNomIdFieldFormulaireLie
      End Property
      
      Public Property Let controlNameLie(strNomControleFormulaireLie As String) 'le nom du controle texte qui recevra la saisie
          strControlName = strNomControleFormulaireLie
      End Property
      
      Public Property Let tableNameLie(strNomTableFormulaireLie As String) 'le nom de la table du formulaire
          strTableFormLie = strNomTableFormulaireLie
      End Property

      Rien de bien particulier à part qu’on utilise un Let au lieu du Set qui est réservé aux objets.

      Nos variables sont devenus des propriétés de la classe.

      Méthode privée

      Une méthode privée permettra d’attendre la fermeture du formulaire de saisie avant de poursuivre l’exécution du code. Nous obtiendrons un déroulement synchrone du processus.

      Ce n’est pas la meilleure méthode mais pour cet exemple cela suffira.

      Private Sub pAttendreFermeture(vlFrmRprt As Object)
      '-----------------------------------------------------------
      ' Procedure   : pAttendreFermeture
      ' Author      : Fabrice CONSTANS
      ' Date        : 20/10/2011
      ' Description : Attend la fermeture de l'objet pour rendre la main
      ' Paramètres  : vlFr est l'objet à controler
      '-----------------------------------------------------------
      On Error GoTo pgAttenteFermeture_Error
         Do
            DoEvents
         Loop While vlFrmRprt.Visible
      
      pgAttenteFermeture_Error:
              On Error GoTo 0
              Exit Sub
      End Sub

      Les évènements

      Les évènements vont être gérés à l’aide de procédure qui deviendront de fait des méthodes de la classe. La différence c’est qu’à aucun moment nous ne devront les appeler pour qu’elles fonctionnent. Ceci grâce au WithEvents de la déclaration et à l’activation des écouteurs.

      Le Notinlist

      On remarque que la signature de la procédure est strictement identique à celle qui pourrait être générée dans un formulaire. Je vous conseille d’ailleurs d’utiliser des signature générée et de les copier dans la classe, vous gagnerez un temps précieux et vous n’aurez pas le risque que cela ne fonctionne pas.

      Public Sub LmCombo_NotInList(NewData As String, Response As Integer) 'signature de la procédure
      '--------------------------------------------------------------------
      ' Procedure : LmContact_NotInList
      ' Author    : Fabrice CONSTANS (MVP)
      ' Date      : 21/01/2016
      ' Purpose   : Nouveau contact, on appelle le formulaire de saisie.
      '------------------------------------------------------------------
      '
      Dim strNouveauNumero As String
      
      On Error GoTo Errsub
      
          If vbYes = MsgBox("Cette valeur n'existe pas. " & _
                 "Souhaitez-vous la créer ?", vbInformation + vbYesNo, _
                                            "classe ccombo") Then
            'ouverture du formulaire en mode Ajout  
            DoCmd.OpenForm strFormNameLie, acNormal, , , acFormAdd
            'on gèle le contrôle reception de la valeur saisie
            Forms(strFormNameLie).Controls(strControlName).Enabled = False
            'on ajoute la valeur saisie dans la liste
            Forms(strFormNameLie).Controls(strControlName).Value = NewData
            'traitement synchrone
            pAttendreFermeture Forms(strFormNameLie)
      
      'Vérification que le valeur existe (peut-être que l'utilisateur a
      ' changé d'avis et a annulé la création
         If dLookUp("nz(" & strIdFieldName & ",-1)", _
             strTableFormLie, strControlName & "=""" & NewData & """") > 0 Then
               'la valeur a été saisi
               Response = acDataErrAdded
            Else
              'la valeur n'a pas été saisi (annulation)  
              Response = acDataErrContinue
              LmCombo.Parent.Undo
           End If
         Else
           'il ne souhaite pas la création
           Response = acDataErrContinue
           LmCombo.Parent.Undo
         End If
      
      Exitsub:
            On Error GoTo 0
            Exit Sub
      Errsub:
             msgbox "cComboBox.LmCombo_NotInList", Err, Erl, Err.Description
      End Sub

      Vous pouvez constater que nous utilisons bien les constantes habituelles pour ce type d’évènement : NewDataacDataErrContinueacDataErrAdded

      Le DblClick

      Cet évènement est plus simple que le précédent puisqu’il ne fait qu’ouvrir le formulaire en mode consultation/modification.
      A la sortie on pense à rafraichir le contenu de la combo au cas ou l’utilisateur a modifié une valeur.
      Il va de soit que pour cette dernière le traitement doit également être synchrone.

      Public Sub LmCombo_DblClick(Cancel As Integer)
      '------------------------------------------------------------------
      ' Procedure : btnEditContact_Click
      ' Author    : Fabrice CONSTANS (MVP)
      ' Date      : 16/02/2016
      ' Purpose   : edit et raffraichit la liste au retour
      '------------------------------------------------------------------
      '
      Dim Id As Long
      
      On Error GoTo Errsub
      
          If IsNull(LmCombo.Column(0)) Then
              MsgBox "Pour créer un item vous devez entrer sa valeur.", _
                     vbInformation + vbOKOnly, "classe ccombo"
              Exit Sub
          End If
      
          Id = LmCombo.Column(0)
      
          DoCmd.OpenForm strFormNameLie, acNormal, , _
                      strIdFieldName & "=" & Id, acFormEdit
      
          pAttendreFermeture Forms(strFormNameLie)
          LmCombo.Undo
          LmCombo.Requery
          LmCombo.value = Id
      
       Exitsub:
         On Error GoTo 0
         Exit Sub 
      
      Errsub:
          msgbox ("cComboBox.LmCombo_DblClick", Err, Erl, Err.Description)
      End Sub

      Voilà la Classe cCombo est prête à être utilisée. Voyons cela dans les faits.

      Utiliser la classe dans un formulaire

      En premier lieu il faut indiquer au formulaire que nous allons utiliser cette classe. Pour cela nous devons utiliser autant de variables qu’il y a de zone de liste à implémenter. Ces variables doivent être globale, donc déclarer immédiatement après les 2 lignes d’Options du module de classe du formulaire.

      Dim cComboClient         As cComboBox

      Dans l’événement Sur Ouverture du formulaire on poursuit la déclaration.

      Set cComboClient = New cComboBox
      
      cComboClient.controlNameLie = "RaisonSociale"
      cComboClient.frmNameLie = "fClient"
      cComboClient.idFieldName = "id_Client"
      Set cComboClient.objComboBox = Me.Controls("lmClient")
      cComboClient.tableNameLie = "tClient"

      New permet de créer l’instance de la classe et ainsi pouvoir valoriser les propriétés.

      Dans cet exemple la liste déroulante se nomme lmClient. Vous pouvez adapter ce code déclaratif à votre propre cas. Vous ne devez jamais modifier la classe pour y intégrer des noms propres à l’application. Une classe doit rester générique sous peine de ne plus être portable.

      Il va de soit qu’à chaque nouvelle liste déroulante à instancier, un code similaire devra être ajouté. C’est tout l’intérêt d’une classe.

      Une fois sauvegardé, vous pouvez utiliser le formulaire.

      Les tests de comportement à faire :

      • Entrer une nouvelle valeur.
      • Double cliquer sur la zone de liste.

      Classe en VBA, quels avantages ?

      Le poids de l’application : On remplace des centaines de lignes par une partie déclarative.

      L’uniformité du comportement : Comme on utilise le même code pour tous les contrôles nous sommes sûr que le comportement sera la même.

      Maintenance et évolution : Si un bug est constaté ou que l’on souhaite faire évoluer le comportement, on intervient dans la classe. La duplication de code n’est plus nécessaire on évite des heures de test, d’erreurs potentielles et de tous les problèmes inhérent à cette pratique.

      Rapidité de conception : Une simple déclaration permet d’exploiter la classe.

      Classe en VBA, quels inconvénients ?

      Gérer toutes les erreurs de l’application : En cas d’erreur non traitée avec les classes chargées se déchargent. Il faudra alors exécuter les déclarations une nouvelle fois.

      Difficulté technique : Pour les non programmeurs, appréhender le concept de classe peut poser des problèmes.

      Complexité : Les classes sont plus complexes à mettre en œuvre qu’un code procédural et évènementiel classique. Si vous traiter un formulaire il est conseillé de concevoir les traitements dans le formulaire puis de créer la classe à partir d’un code fonctionnel. Si c’est un classe standard créé le code dans un module standard, cela vous permettra de tester rapidement votre création, ensuite le transformer-la en classe.

      Conclusion

      J’espère que cette incursion dans les classes vous aura plu et qu’elle vous donnera des idées d’implémentations pour vos développements. N’hésitez pas à commenter ce billet.

      ACCESS : Audit de parc informatique – RPNG Access 97

      Dans les années 90, les produits d’audit de parc informatique étaient rares (3 identifiés à l’époque) et très onéreux (plusieurs milliers de dollars, hors assistance obligatoire). Quand on compte 3000 postes PC et une petite centaine de MAC répartis sur 3 sites sensibles, faire l’inventaire des logiciels installés est une véritable mission chronophage.

      En quelques jours, avec Microsoft Access 97, j’ai créé un outil d’analyse des postes PC distants. Je me suis fixé les limites d’acceptabilités suivantes :

      • rapidité de collecte d’information du poste pour ne pas gêner l’utilisateur
      • possibilité de faire la collecte en dehors de certaines plages horaire
      • création de persistance pour faciliter l’analyse rapide des données
      • fonctionner en autonomie pour permettre à la petite équipe de se consacrer à l’analyse des données collectées
      • utiliser un RAD (Access est un RAD !) pour pouvoir faire évoluer l’outil suivant les demandes et limiter les coûts d’un produit jetable.

      J’ai utilisé des commandes systèmes pour collecter les informations de chaque poste et persisté sur une base Jet (la base de données interne d’Access sur cette version) parfaitement dimensionnée pour le travail à réaliser.

      Traitements

      • Les nouveaux logiciels identifiés ont fait l’objet d’un traitement d’identification manuel
      • Ceux déjà connus sont automatiquement identifiés
      • Plus la banque de données de référence (liste des logiciels) progresse moins les interventions manuelles sont nécessaires
      • Les postes inaccessibles (RH, Direction…) ont été traités manuellement, puis intégrés au processus automatisé
      • Les 200 premiers postes ont permis d’automatiser à 99 % le traitement des suivants
      • Une centaine de Mac nécessitent une intervention et une saisie manuelle, soit un rapport équivalent à 6,7 %, similaire à celui observé précédemment.

      Grâce à l’enrichissement progressif de la base de référence et à l’apprentissage issu des premiers traitements manuels, le processus d’identification des logiciels a atteint un haut niveau d’automatisation, réduisant significativement les interventions humaines et permettant un traitement fiable et quasi intégral des postes restants.

      Les résultats

      Les résultats ont dépassé les attentes du client. En deux semaines, avec une équipe restreinte de deux personnes, nous avons réalisé un inventaire précis et fiable du parc.

      Les résultats ont été consolidés dans un fichier Excel structuré, répertoriant les logiciels identifiés, leur taux de présence sur le parc, leur typologie fonctionnelle et leur localisation précise.

      ACCESS : Plus qu’un RAD il est au cœur de l’intégration

      Microsoft Access est souvent perçu comme un outil de base de données local. Pourtant, grâce à VBA et ses multiples bibliothèques, sa capacité à interagir avec les SGBDR du marché et son moteur ACE, il peut jouer un rôle central dans des architectures plus complexes. Il devient alors un véritable hub de connectivité entre différents systèmes.

      Un exemple de connectivité Full Access :

      • Un CRM réalisé avec Access travaille avec MariaDb pour les données.
      • Il utilise ACE pour stocker certains paramètres locaux.
      • Une partie de données du CRM sont issus d’un logiciel métier sous PostgreSQL.
      • Un utilitaire autonome permet de faire une synchro client à heure fixe ou à la demande.

      Un exemple d’utilisation sans ACE :

      • Un logiciel de création de modèle statistique réalisé avec Access.
      • Il utilise une base de données MariaDb hébergé.
      • Le logiciel récupère des données INSEE de plusieurs millions de lignes.
      • Les formats hétérogènes de ces données (CSV, DBase…) sont traités et envoyés vers MariaDb.
      • Le logiciel permet de créer des modèles statistiques réutilisables.
      • Le résultat est injecté dans des feuilles Excel pour créer des graphiques des tableaux de chiffre en vue de créer des présentations.

      MS Access, un RAD central

      MS Access a de multiples casquettes :

      • point de convergence pour des données hétérogènes (SQL, JSON, CSV…).
      • Client léger connecté à une base de données serveur.
      • Utilitaire autonome
      • Client lourd connecté à ACE en local ou intranet.

      Grâce aux formulaires et états dotés de composants riches et paramétrables, l’utilisateur peut interagir avec les bases de données connectées via les formulaires et les états.

      Important : ACE n’est pas compatible avec un cloud partagé (dropbox, onedrive…)

      VBA un vrai langage

      Le nuage VBA représente les interfaces de programmation qu’Access peut exploiter :

      • Windows API : pour interagir avec le système d’exploitation (fichiers, registres, processus…)
      • .NET COM : pour appeler des objets COM exposés par des bibliothèques .NET
      • API Web : pour envoyer des requêtes HTTP vers des services RESTful ou SOAP.
        Ces connexions permettent à Access de sortir de son périmètre local et d’interagir avec des services distants ou des composants système.
      • VBScript remplacé par PowerShell : véritable couteau suisse de Windows.

      Access et les bases de données

      Access peut aussi se connecter à des bases de données externes via ODBC ou des pilotes spécifiques :

      • MariaDb
      • SQL Server
      • PostgreSQL
      • Oracle
        Ces connexions permettent de centraliser les données dans Access tout en conservant la puissance des SGBD professionnels. Ces bases de données peuvent être locales, sur un serveur intranet ou totalement hébergées.

      Le moteur ACE

      Le moteur ACE (Access Connectivity Engine) est le moteur de base de données intégré à MS Access. Il permet :

      • L’accès aux fichiers de données accdb
      • L’utilisation d’Access comme source OLEDB dans d’autres applications
      • L’export/import de données vers Excel, CSV, etc.
      • ACE est aussi utilisé dans des contextes sans Access installé, comme dans des scripts PowerShell ou des applications .NET.

      Conclusion

      Access n’est pas juste un outil de bureau pour les petites bases de données. C’est un véritable orchestrateur, capable de dialoguer avec des APIs, des bases de données distantes, et des composants système. Grâce à VBA et ODBC il devient un pont entre le local et le cloud, entre l’utilisateur et les données.

      ACCESS : Pourquoi Ms Access ?

      Si vous entendez quelqu’un vous dire que MS Access est une base de données, c’est que votre interlocuteur ne le connaît pas ou ne s’est jamais aventuré au-delà de l’utilisation des tables et des requêtes. MS Access n’est pas une base de données (SGBDR). Il est plutôt à ranger dans la catégorie des RAD – acronyme de « Rapid Application Development » ou « Système de Développement Rapide d’Application » en Français.

      C’est donc un produit qui intègre tous les composants nécessaires à la création « rapide » d’applications. Je mets le terme « rapide » entre guillemets, car bien qu’Access facilite la conception d’applications simples, concevoir une application complexe demande une certaine expertise.

      Pourquoi choisir Access ?

      Les utilisateurs qui choisissent Access le font généralement pour les bonnes – ou les mauvaises – raisons suivantes :

      • Disponible dans la version d’Office Professionnel ou en produit indépendant,
      • Dispose d’un Runtime
      • Facilité d’installation par rapport à d’autres produits de développement qui demande des compétences système et DBA.
      • Prise en main intuitive de l’interface graphique pour créer des tables, des relations et des requêtes,
      • Possibilité de lier des tables issues d’autres SGBDR,
      • Ensemble d’objets pour aller plus loin qu’une simple base de données,
      • Parfaitement intégré dans le monde Office et Windows.

      Parmi les raisons contradictoires certaines sont factuelles, d’autres relèvent d’opinions, au pire des consensus fondés sur une méconnaissance du produit :

      • Édité par Microsoft (donc non libre),
      • Disponible uniquement sous Windows,
      • Base de données fichier (ACE), pas serveur,
      • ACE Limitée à 2 Go par fichier
      • Pas orienté Web
      • Méconnaissance de VBA (vu comme un langage de macros)
      • Confusion avec Excel (alors que ce sont 2 produits incomparables)
      • Mauvaise réputation en France (perçu comme un produit « bureautique »),

      Vrai ou faux

      Autant Ms Access n’est pas libre autant ce que vous produisez peut l’être, tout dépend de la licence que vous choisissez.

      En effet, Access n’est disponible que sous Windows… mais fonctionne parfaitement sur une machine virtuelle.

      ACE est une base de données fichier. Elle est très performante et a le mérite d’être parfaitement intégrée à Access. Cependant vous pouvez utiliser la base de données que vous souhaitez. Personnellement, tous mes projets d’envergure s’appuient sur Sql Server, MariaDb, PostgresSQL J’ai également travaillé sur de très grosses applications sur Oracle et Sql Server.

      Oui ACE est limité à 2 Go. Mais vous êtes libre de choisir la base de données la plus adaptée.

      Access n’est pas WEB. Si vous devez faire un site ou une application WEB ce n’est pas le bon produit. Il m’est arrivé de discuter avec des clients qui souhaitaient absolument une techno WEB… sans savoir pourquoi. En analysant le besoin je me suis rendu compte que ce besoin n’était pas justifié.

      VBA est un langage accessible mais qui demande de la rigueur sur des projets professionnels. Ceux qui confondent VBA et macro ne connaissent pas le sujet.

      Excel et Access n’ont rien à voir. L’un est un tableur qui manipule des données alors que l’autre est un RAD. Oui on peut parfaitement créer une IHM avec Excel, cependant il ne gérera jamais une base de données.

      MS Access et sa mauvaise réputation en France

      Cette mauvaise réputation est héritée d’une époque (fin des années 90 – début 2000) où les DSI voyaient Access comme une menace : des utilisateurs métiers créaient leurs propres outils, sans passer par les services informatiques. Ce que les DSI oubliaient c’est que ces utilisateurs :

      • maîtrisaient leur métier,
      • savaient ce dont ils avaient besoin
      • avaient un excellent taux de réactivité

      Certes ils péchaient d’un manque d’expérience en développement. Cela donnait des applications parfois bancales… mais toujours utiles puisque 10 ans plus tard elles étaient toujours là.

      Ms Access et ses objets

      Les objets pour créer une application sont les suivants :

      • Tables (internes ou liées)
      • Requêtes SQL
      • Formulaires (saisie, consultation, suppression, navigation…)
      • Etats d’impression,
      • Macros (automatisation simpliste)
      • Modules pour le langage VBA. (automatisation avancée)

      Access et les autres SGBDR

      Bien que la plupart des applications utilisent un SGBDR pour la persistance, on peut créer des applications sans jamais avoir besoin d’une base de données. A contrario on peut avoir besoin d’une application standalone, sans aucune IHM et qui fonctionne de manière autonome.
      Une application de synchronisation intelligente de données entre SGBDR hétérogènes en est le parfait exemple.
      En règle générale Access permet de créer un front-end léger couplé à une base ACE, SQL Server, PostgresSQL, MariaDb.

      Le pire côtoie le meilleur

      Le pire c’est une appli conçue à la va-vite, sans modèle de données solide. Les adeptes du QDD (Quick & Dirty & Disposable, soit Vite, Sale et Jetable en français) en sont friand. Une fois le « jetable » installé, il est difficile de s’en passer, la réécriture étant couteuse et l’informatique le parent pauvre de l’industrie, on retrouve ces applications 15 ans plus tard pleinement fonctionnelles. Leur maintenance se fait souvent dans la douleur et les larmes et Access est déclaré comme un produit d’amateurs.

      Le meilleur s’appuie sur les concepts qui régissent tous les logiciels. Elle s’appuie sur un choix de base de données pertinent, elle est maintenable, évolutive et répondant aux vrais besoins métier.

      En résumé

      Access est sous-estimé, mal compris, mais redoutablement efficace quand il est bien maîtrisé. Beaucoup ont tenté de migrer des applications Access vers des technos « à la mode » sans retrouver la puissance et l’ergonomie des composants d’Access. L’application est alors vu pas les utilisateurs comme ces livres inutiles qui ornent l’étagère la plus haute des bibliothèques.

      VBA : Split()

      Split() est une fonction pratique et largement utilisée. A partir d’une chaine de texte elle renvoie un objet Tableau (Array) composé de ses éléments en utilisant le séparateur désigné.

      Par exemple :

      Dim monTexte as String
      Dim monTableau as Variant
      montexte = "Bonjour je suis Sam"
      
      monTableau = Split(montexte, " ")

      Dans l’exemple ci-dessus le tableau monTableau contiendra :
      montableau(0) >> « Bonjour »
      montableau(1) >> « je »
      montableau(2) >> « suis »
      montableau(3) >> « Sam »

      On peut donc utiliser un boucle (Do, While, For) pour parcourir le tableau, comme on peut utiliser l’indice comme c’est fait dans la liste précédente. Ci-dessous un exemple avec une boucle For.

      Dim i As Long
      Dim vArray As Variant
      vArray = Split("Bonjour je suis Sam", " ")
      For i = 0 To UBound(vArray)
          Debug.Print vArray(i)
      Next

      Split renvoie un objet tableau, Split() est un tableau

      Si Split est un tableau on peut l’utiliser en tant que tel sans l’affecter à une variable.
      Dans l’exemple suivant on récupère directement l’item du tableau.

      Dim monPrenom as String
      monPrenom = Split(montexte, " ")(3)

      La variable monPrenom vaudra :
      monPrenom >> « Sam »

      Le paramètre (3) accolé directement à la fonction Split() peut vous paraitre étrange à première vue pourtant ce n’est ni plus ni moins que l’indice du tableau.

      Conclusion

      Lorsqu’on veut récupérer une valeur et que l’on connait avec certitude sa position, utiliser cette syntaxe est plus rapide, cependant lorsqu’on veut utiliser plus d’une valeur, l’utilisation classique est plus performante.

      ACCESS : Erreur 3010 avec DAO.CreateTableDef()

      Ce problème est constaté le 16/01/2023

      Le CreateTabledef() de DAO ne fonctionne plus comme avant !
      Avec un :

      Set td = db.CreateTableDef(tblname, dbAttachSavePWD, tblname, strConnexionOdbc)


      On obtient une erreur 3010, la table existe déjà, mais non elle n’existe pas, ni dans la liste des objets, ni dans TableDefs et encore moins dans MsysObjects.

      Quand on observe td on peut voir que Name et égal à SourceTableName
      Le contournement est possible :

      Set td = db.CreateTableDef()
      td.Name = strTable
      td.SourceTableName = tblname
      td.Connect = strConnexionOdbc
      td.Attributes = dbAttachSavePWD


             Ou encore :

      DoCmd.TransferDatabase acLink, "ODBC Database", strConnexionOdbc, acTable, tblname, strTable, False, False
      

      Ce problème a été constaté avec une application qui fonctionne avec ce code depuis 2016, et sur une fonctionnalité qui est utilisée quotidiennement.

      Le pilote ODBC n’est pas en cause, des tests ont été fait dans ce sens.