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

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Bases de données Delphi Discussion :

[SQLITE] [FireDAC] Recherche avec Diacritiques


Sujet :

Bases de données Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Inscrit en
    Novembre 2002
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 33
    Par défaut [SQLITE] [FireDAC] Recherche avec Diacritiques
    Bonjour
    je souhaite retrouver dans une base SQlite toutes les valeurs avec ET sans accents et autres diacritiques
    je cherche "Jérémy", je souhaiterais qu'un LIKE '%JEREMY%' me ramène jeremy, jérémy, JÉRÉMY...

    Mauvaise solution :
    Je le faisais précédemment avec Delphi 7 et les composants Zeos comme ceci :
    WHERE ((REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(author,'à','a'),'á','a'),'å','a'),'â','a'),'ä','a'),'ã','a'),'é','e'),'è','e'),'ë','e'),'ê','e'),'ì','i'),'í','i'),'î','i'),'ï','i'),'ò','o'),'ø','o'),'ô','o'),'ö','o'),'õ','o'),'ú','u'),'ù','u'),'ü','u'),'û','u'),'ý','y'),'ÿ','y') LIKE '%JEREMY%')
    OR (REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(author,'Á','A'),'Â','A'),'Ã','A'),'Ä','A'),'Å','A'),'È','E'),'É','E'),'É','E'),'Ê','E'),'Ë','E'),'Ì','I'),'Í','I'),'Î','I'),'Ï','I'),'Ì','I'),'Ò','O'),'Ó','O'),'Ô','O'),'Õ','O'),'Ö','O'),'Ù','U'),'Ú','U'),'Û','U'),'Ü','U') LIKE '%JEREMY%'))
    Cela fonctionnait... lentement! Je le mets dans le message pour servir éventuellement.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
    ////////////////////////////////////////////////////////////////////////////////
    // Pour créer les chaînes, avec la recherche en "aValue"
    function SqlReplace1(aValue: string): string;
    Var S1,S2,S3 : string;
        i : integer;
    begin
    S1 :='àáåâäãéèëêìíîïòøôöõúùüûýÿ';
    S2 :='aaaaaaeeeeiiiiooooouuuuyy';
    S3 :='' ;
    for i:= 1 to length(S1) do
       S3:=S3+'REPLACE(';
     S3:=S3+aValue;
    for i:= 1 to length(S1) do
       S3:=S3+','+quotedstr(S1[i])+','+quotedstr(S2[i])+')';
    result := S3+' ';
    end;
    ////////////////////////////////////////////////////////////////////////////////
    function SqlReplace2(aValue: string): string;
    Var S1,S2,S3 : string;
        i : integer;
    begin
     S1 :='ÁÂÃÄÅÈÉÉÊËÌÍÎÏÌÒÓÔÕÖÙÚÛÜ';
     S2 :='AAAAAEEEEEIIIIIOOOOOUUUU';
    S3 :='' ;
    for i:= 1 to length(S1) do
       S3:=S3+'REPLACE(';
     S3:=S3+aValue;
    for i:= 1 to length(S1) do
       S3:=S3+','+quotedstr(S1[i])+','+quotedstr(S2[i])+')';
    result := S3+' ';
    end;
    Problème actuel
    J'ai implémenté le code préconisé par RAD STUDIO et FireDAC
    (exemple : FireDAC.Phys.SQLite.TFDSQLiteCollation)
    et modifié le code en tenant compte de ce message (Epées et FireDac SQLite), mais ça ne fonctionne pas.
    J'ai modifié certains paramètres du composant sans succès.
    Voila le résultat, je voudrais avec les 3 recherches avec le mot "PERE"


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    procedure TForm1.FormCreate(Sender: TObject);
     
    begin
      with FDConnection do
        if Connected then Connected := False;
     
      SQLitePhysDriverLink:= TFDPhysSQLiteDriverLink.Create(nil);
      SQLiteCollation:= TFDSQLiteCollation.Create(nil);
     
      With SQLiteCollation do begin
        Active := False;
        DriverLink := SQLitePhysDriverLink;
        CollationName := 'UTF16NoCase';
        Flags := [sfLingIgnoreCase,sfLingIgnoreDiacritic,sfIgnoreCase];
        Active := True;
      end;
     
    {Ouverture de FDConnection après activation de SQLiteCollation }
      with FDConnection do begin
        Params.Add ('DriverID = SQLite');      {Inutile ici}
        Params.Add ('OpenMode = CreateUTF8');  {Idem}
        Connected := True;
      end;
    end;
     
    procedure TForm1.btnFindClick(Sender: TObject);
    begin
      sSql.Clear;
      sSql.add('SELECT CAST(AUTHOR_SORT AS varchar(255)) AS AUTEUR,');
      sSql.add('CAST(TITLE AS varchar(255)) AS TITRE');
      sSql.add('FROM "BOOKS" ');
      sSql.add('WHERE');
      sSql.Add('BOOKS.AUTHOR_SORT LIKE '+quotedstr('%'+edAuteur.Text+'%'));
      sSql.Add('ORDER BY AUTHOR_SORT, TITLE');
      memoOut.Lines.Assign(sSql);
      qryBooks.Open(sSql.Text);
      caption := inttostr(DSBooks.DataSet.recordcount);
    end;
    Merci de votre aide, ou de me dire si je suis dans la bonne direction au moins.

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 14 086
    Par défaut
    Il y a très longtemps, j'avais constitué un dictionnaire de mot pour aider un moteur de recherche avec une forme "tel que" et une forme majuscule sans accents et une sans accents sans ligature, 800000 mots pointant sur 6000 blocs de texte.
    Ligature soit comme dans cœur ou œdème
    ces 6000 textes faisant parti d'un thésaurus maintenu que lors d'un changement de version de ce dictionnaire, soit une fois par an. on pouvait donc se permettre ce choix technique

    Mais si l'on souhaite une recherche fulltext sans devoir générer un dictionnaire qui peut devenir très lourds si l'on n'a pas un ensemble "fini" de texte, j'ai codé un rapide recherche dans des XML récemment, je n'ai pas encore supporté la gestion des accents, ces XML pouvant varier d'une base à un autre, varier souvent, il est inenvisageable de maintenir un dictionnaire (même si via un jeu de TRIGGER cela sera faisable)

    Tu as repris l'exemple UTF16NoCase, je l'aurais appelé différemment mais cela ne doit pas avoir d'impact

    sfLingIgnoreDiacritic "Ignore linguistiquement les caractères sans espace." / "Linguistically ignore non-spacing characters." selon documentation
    Cela veut dire quoi sans espace ? une erreur pour pour accent ?
    une mauvaise reprise de sfIgnoreNonSpace ?

    Sinon dans ton SQL, tu ne devrais pas écrire ceci ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
      sSql.Text := 
        ' SELECT CAST(AUTHOR_SORT AS varchar(255)) AS AUTEUR) ' +
        ' CAST(TITLE AS varchar(255)) AS TITRE ' +
        ' FROM "BOOKS"  ' +
        ' WHERE BOOKS.AUTHOR_SORT LIKE '+quotedstr('%'+edAuteur.Text+'%')
        ' ORDER BY AUTHOR_SORT, TITLE ' +
        ' COLLATE UTF16NoCase ' ; // Faut bien lui dire si tu l'utilises ou pas cette collation
    J'ignore combien de colonne ta recherche doit supporter mais je tenterais l'astuce du TRIGGER, lors de la modification TITLE ou AUTHOR, je stockerais dans une colonne TITLE_C et AUTHOR_C une version épurée pour le moteur de recherche pour n'utiliser qu'un seul LIKE comme ci-dessus, l'avantage que l'on peut indexer les colonnes épurées donc améliorer la recherche

    Sinon faire LIKE OR LIKE, en passant la variante brute et la variante sans accent avec une comparaison insensible à la casse (soit par Collation soit par UPPER)
    Il y a plein d'approche, la volumétrie obligeant à faire des choix

    Voir aussi si tu peux utiliser un Paramètre, en SQL Server récemment, le Champ LIKE :pFilterValue en ADO AADOQuery.Parameters.ParamByName(AFilter.ParamName).Value := AFilter.FieldValue; est passé tout seul.
    Tu peux ainsi préparer la requête et bind avec différentes valeurs, encore du temps gagné
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre averti
    Inscrit en
    Novembre 2002
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 33
    Par défaut
    Merci de la réponse.
    j'avais essayé avec le collate dans le select... puis enlevé car j'avais lu (où?) qu'un seul suffit et que celui dans le select est prioritaire.

  4. #4
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 596
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 596
    Billets dans le blog
    65
    Par défaut
    Bonjour,

    AMHA le problème serait plutôt dans la clause WHERE.

    Voila le résultat, je voudrais avec les 3 recherches avec le mot "PERE"
    si je me fie à ces images écrans il s'agit plus d'un LOWER(TITLE) LIKE ....
    ensuite faudrait-il comprendre que la recherche est le cumul des 3 images ?

    [EDIT] je n'avais pas intégré le début de la discussion
    je souhaite retrouver dans une base SQlite toutes les valeurs avec ET sans accents et autres diacritiques
    je cherche "Jérémy", je souhaiterais qu'un LIKE '%JEREMY%' me ramène jeremy, jérémy, JÉRÉMY...

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     SELECT CAST(AUTHOR_SORT AS varchar(255)) AS AUTEUR,
                     CAST(TITLE AS varchar(255)) AS TITRE
                     FROM "BOOKS"
                     WHERE
                     BOOKS.AUTHOR_SORT LIKE '+quotedstr('%'+edAuteur.Text+'%'
                     ORDER BY AUTHOR_SORT, TITLE;
    pourquoi ces CAST et " " pour le nom de table ?

    j'aurais écrit la requête différement et utiliser une macro
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     SELECT AUTHOR_SORT  AUTEUR,
                                TITLE  TITRE
                     FROM BOOKS
                     &clausewhere
                     ORDER BY AUTHOR_SORT, TITLE;

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    ssql.close;
    if edtAuteur.text.isempty  // if Length(trim(edtauteur.text))=0
                 then  ssql.macrobyname('clausewhere').clear  
                 else   ssql.macrobyname('clauserwhere').asraw:='WHERE AUTHOR_SORT LIKE '+quotedstr('%'+edAuteur.Text+'%');
    ssql.Open();
    pour rependre un paramètre comme le suggère ShaiLetroll j'utiliserai
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     SELECT AUTHOR_SORT  AUTEUR,
                                TITLE  TITRE
                     FROM BOOKS
                     where Like(:parametre,AUTHOR_SORT)
                     ORDER BY AUTHOR_SORT, TITLE;
    car firedac ne sais pas traiter (à ma connaissance) le LIKE :parametre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    var like : String;
    like:='%'+edAuteur.Text+'%';
    ssql.Open('',[like]);


    Bien évidemment, pour moi, c'est le edAuteur.text que je traiterai pour les caractères accentués (je me demande d'ailleurs s'il n'y a pas une fonction toute faite pour ça)

    Encore un essai à programmer dans mon agenda du week-end

  5. #5
    Membre averti
    Inscrit en
    Novembre 2002
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 33
    Par défaut
    Citation Envoyé par SergioMaster Voir le message
    si je me fie à ces images écrans il s'agit plus d'un LOWER(TITLE) LIKE ....
    ensuite faudrait-il comprendre que la recherche est le cumul des 3 images ?
    Merci
    je précise ma démarche par rapport à la source de recherche.
    Il faut rentrer une liste de noms propres dans une table de noms sans faire de doublons.

    Les noms dans les deux listes peuvent être orthographiés avec ou sans diacritiques et majuscule :
    éric, eric, Eric, Éric par exemple. (et des noms étrangers avec des diacritiques non français : á, í, ö, ø... qui ont perdu leur diacritique d'un coté ou de l'autre)
    Je veux tous les trouver.
    Par principe mais je me trompe peut-être je filtre les diacritiques dans la source et je cherche "ERIC" pour trouver éric, eric, Eric, Éric.

    Pour filtrer les diacritiques, voici 2 fonctions qui peuvent être utiles. Faire un UPPERCASE éventuel.
    voir aussi sur ce forum: Enlever les accents d'une chaîne : solution
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    Function DTStringRemoving(s:string):string;
    var i:integer;
    begin
     i:=1;
     while i<=length(s) do
      begin
       case s[i] of 'Œ','œ':begin
                             s[i]:='o';
                             insert('e',s,i+1);
                             inc(i);
                            end;
                    'Æ','æ':begin
                             s[i]:='a';
                             insert('e',s,i+1);
                             inc(i);
                            end;
                    'Š','š':s[i]:='s';
                    'Ç','ç':s[i]:='n';
                    'Ñ','ñ':s[i]:='n';
                    'Ð':s[i]:='d';
                    'Ÿ','Ý','ý','ÿ':s[i]:='y';
                    'À'..'Å','à'..'å':s[i]:='a';
                    'È'..'Ë','è'..'ë':s[i]:='e';
                    'Ì'..'Ï','ì'..'ï':s[i]:='i';
                    'Ò'..'Ö','Ø','ò'..'ö','ø':s[i]:='o';
                    'Ù'..'Ü','ù'..'ü':s[i]:='u';
         end;
       inc(i);
       end;
     Result:=s;
    end;
    // autre fonction windows
    function RemoveDiacritics(aValue: string): string;
     var
        Tmp :string;
        Len :integer;
        i   :Integer;
      begin
        Len := FoldString(MAP_COMPOSITE, PChar(aValue), -1, nil, 0);
        SetLength(Tmp, Len);
        FoldString(MAP_COMPOSITE, PChar(aValue), -1, PChar(Tmp), Len);
        Result := '';
        for i := 1 to Length(Tmp) do
          if IsCharAlphaNumeric(Tmp[i]) OR (Tmp[i] = ',') OR (Tmp[i] = ' ') then
             Result := Result +Tmp[i];
      end;

  6. #6
    Rédacteur/Modérateur

    Avatar de SergioMaster
    Homme Profil pro
    Développeur informatique retraité
    Inscrit en
    Janvier 2007
    Messages
    15 596
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 69
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2007
    Messages : 15 596
    Billets dans le blog
    65
    Par défaut
    Re,
    Voilà ma curiosité a été piquée et du coup j'ai fait plusieurs essais

    tout d'abord une fonction pour ôter les accents (à améliorer)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function RemoveAccents(const WS: String): AnsiString;
    const
      CodePage = 20127; //20127 = us-ascii
    begin
      SetLength(Result, WideCharToMultiByte(CodePage, 0, PWideChar(WS),
        Length(WS), nil, 0, nil, nil));
      WideCharToMultiByte(CodePage, 0, PWideChar(WS), Length(WS),
        PAnsiChar(Result), Length(Result), nil, nil);
    end;
    par la suite j'ai lu ceci
    As specified in the documentation, the built-in LIKE and GLOB operators always uses the NOCASE or BINARY collation.
    https://www.sqlite.org/lang_expr.html#like
    Donc si j'en crois ceci le like ne tiendra pas compte d'une collation quelconque (que ce soit une déclarée au sein de SQLite ou du FDphysDriver), et,je confirme, LIKE est case insensitive par défaut

    j'ai donc tenté l'expérience en créant ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    procedure TForm3.compareprocedure(ASender: TObject; len1: Integer;
      str1: Pointer; len2: Integer; str2: Pointer; var AResult: Integer);
    begin
    showmessage('test');
    // je ne sais pas trop comment traiter str1,str2
    Aresult:=0;
    end;
     
    procedure TForm3.FDPhysSQLiteDriverLink1DriverCreated(Sender: TObject);
    begin
      SQLiteCollation:= TFDSQLiteCollation.Create(nil);
     
      With SQLiteCollation do begin
        Active := False;
        DriverLink := FDPhysSQLiteDriverLink1;
        CollationName := 'SANSACCENT';
        SQLiteCollation.CollationKind:=scCustomUTF16;  // appel à une collation particulière
        SQLiteCollation.OnCompare:=CompareProcedure; // procedure qui permet la comparaison 
        Active := True;
      end;
    end;
    Si je fait un SQL 'ORDER BY COLACCENT COLLATE SANSACCENT' je rentre bien dans ma procédure de comparaison
    par contre un 'WHERE COLACCENT COLLATE SANSACCENT LIKE ...' ne rentre pas dedans

  7. #7
    Membre averti
    Inscrit en
    Novembre 2002
    Messages
    33
    Détails du profil
    Informations forums :
    Inscription : Novembre 2002
    Messages : 33
    Par défaut
    Citation Envoyé par SergioMaster Voir le message
    Si je fait un SQL 'ORDER BY COLACCENT COLLATE SANSACCENT' je rentre bien dans ma procédure de comparaison
    par contre un 'WHERE COLACCENT COLLATE SANSACCENT LIKE ...' ne rentre pas dedans
    Toutes mes recherches de COLLATE menaient à des problèmes de tris avec accents (ORDER BY).
    COLLATE ne traite peut-être les recherches avec "where" ?

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Recherche avec Locate option lopartialKey
    Par jeje.r dans le forum Bases de données
    Réponses: 4
    Dernier message: 21/12/2022, 10h56
  2. SQLite+Firedac, problème avec des strings !
    Par SergioMaster dans le forum Bases de données
    Réponses: 3
    Dernier message: 27/05/2016, 11h57
  3. Recherche avec des accents!?
    Par mona dans le forum Access
    Réponses: 3
    Dernier message: 14/06/2005, 20h36
  4. recherche avec findnearest
    Par souad26 dans le forum Bases de données
    Réponses: 2
    Dernier message: 18/11/2004, 20h58
  5. Enlever la surbrillance lors d'une recherche avec vi
    Par sekiryou dans le forum Applications et environnements graphiques
    Réponses: 8
    Dernier message: 04/03/2004, 13h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo