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

SQL Oracle Discussion :

Fonction REGEXP_LIKE et {n,m} qui ne marche pas de façon "logique" [11gR2]


Sujet :

SQL Oracle

  1. #1
    Membre émérite
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Avril 2013
    Messages
    1 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 993
    Points : 2 499
    Points
    2 499
    Par défaut Fonction REGEXP_LIKE et {n,m} qui ne marche pas de façon "logique"
    Bonjour,

    Je demande votre aide car je m'arrache un peu les cheveux avec les expressions régulières.
    Le sujet est passionnant mais j'ai du mal à comprendre comment fonctionne la syntaxe avec {}.

    Exemples : j'utilise une table EMP avec une colonne salaire. Elle est un peu bizarre (salaire de 1 par exemple) mais c'est juste pour les besoins du test
    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
    USER01@dtx> select nom, salaire from emp order by salaire
    NOM                     SALAIRE
    -------------------- ----------
    DUBOIRES                      1
    DDDUBOIRES                   12
    FRADUBOIS                   123
    DDFRADDUBOISDD             1234
    TIMBAUT                   77800
    TIMBAUT                   77800
    a aab aaabc aaaabcd       80500
    DUBOI                     80500
    TIMBAUT                   80500
    DUBOIS                    80500
    DUBOISEAU                 80500
    DRABAUD                   88500
    DRABAUD                   88500
    TIMBAUT                   88500
    TIMBAUT                  100000
    TIMBAUD                  100000
    TIMBAUT                  100000
    DRABAUD                  100000
    DRABAUD                  100000
    dUBOIS                   110000
    DUBOI                    200000
    DUBOIS12EAU              200000
    DUBOIS                   200000
    DUBOIS                   200000
    TIMBAUT                  200000
    DUBOIS                   200000
    DRABAUD                  210500
    1A
    2258AA
     
    29 rows selected.
    Si j'utilise {n,m} ça fonctionne pour le minimum mais pas pour le maximum --> les champs de 0, 1 et 2 chiffres sont éliminés mais ceux de plus de 5 chiffres sont présents ce qui est anormal!
    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
    USER01@dtx> select nom, salaire from emp where regexp_like ( salaire, '[0-9]{3,5}') order by salaire
    NOM                     SALAIRE
    -------------------- ----------
    FRADUBOIS                   123
    DDFRADDUBOISDD             1234
    TIMBAUT                   77800
    TIMBAUT                   77800
    a aab aaabc aaaabcd       80500
    DUBOISEAU                 80500
    DUBOI                     80500
    DUBOIS                    80500
    TIMBAUT                   80500
    TIMBAUT                   88500
    DRABAUD                   88500
    DRABAUD                   88500
    DRABAUD                  100000
    TIMBAUT                  100000
    DRABAUD                  100000
    TIMBAUD                  100000
    TIMBAUT                  100000
    dUBOIS                   110000
    DUBOIS                   200000
    TIMBAUT                  200000
    DUBOIS                   200000
    DUBOIS                   200000
    DUBOIS12EAU              200000
    DUBOI                    200000
    DRABAUD                  210500
     
    25 rows selected.

    Si j'utilise {n} ça fonctionne de la même façon que {n,m} --> les champs de 0, 1 et 2 chiffres sont éliminés mais ceux de plus de 5 chiffres sont présents!
    Je connais la notion de GREEDY (voracité) pour les expressions régulières mais quand je dis {n} je croyais que ça voulait dire "Un nombre EXACT de caractères" alors que là c'est comme le minimum.
    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
    USER01@dtx> select nom, salaire from emp where regexp_like ( salaire, '[0-9]{3}') order by salaire
    NOM                     SALAIRE
    -------------------- ----------
    FRADUBOIS                   123
    DDFRADDUBOISDD             1234
    TIMBAUT                   77800
    TIMBAUT                   77800
    a aab aaabc aaaabcd       80500
    DUBOISEAU                 80500
    DUBOI                     80500
    DUBOIS                    80500
    TIMBAUT                   80500
    TIMBAUT                   88500
    DRABAUD                   88500
    DRABAUD                   88500
    DRABAUD                  100000
    TIMBAUT                  100000
    DRABAUD                  100000
    TIMBAUD                  100000
    TIMBAUT                  100000
    dUBOIS                   110000
    DUBOIS                   200000
    TIMBAUT                  200000
    DUBOIS                   200000
    DUBOIS                   200000
    DUBOIS12EAU              200000
    DUBOI                    200000
    DRABAUD                  210500
     
    25 rows selected.
    Pour avoir le résultat avec le nombre exact de caractères je dois utiliser ^et $ mais cela ne me satisfait pas car c'est du bidouillage et, surtout, cela ne m'explique pas comment fonctionne la syntaxe avec {}.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    USER01@dtx>select nom, salaire from emp where regexp_like ( salaire, '^[0-9]{3}$') order by salaire
    NOM                     SALAIRE
    -------------------- ----------
    FRADUBOIS                   123
    Bon, si quelqu'un pouvait m'expliquer ce problème... si c'est la notion de greedy (voracité), merci de me dire comment rendre l'expression régulière non greedy pour la syntaxe {}.
    DBA Oracle
    Rédacteur du blog : dbaoraclesql.canalblog.com

  2. #2
    McM
    McM est déconnecté
    Expert éminent

    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Juillet 2003
    Messages
    4 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Juillet 2003
    Messages : 4 580
    Points : 7 740
    Points
    7 740
    Billets dans le blog
    4
    Par défaut
    Parce que tu n'as pas saisi la notion de base des regexp qui recherche la "présence" à un endroit (n'importe lequel)

    La doc Oracle dit :
    {m,n} Matches at least m times but no more than n times
    Utilisé seule cette fonction [0-9]{3} est correcte pour '1234567891561545456465' car '123' correspond à la demande, comme 234, 345, 456 etc...

    Par contre là où ça devient utile, c'est quand c'est combiné à d'autres restrictions

    exemple, je veux tout ce qui correspond à "DE", puis 1 ou 2 "a", suivi d'un "1" => 'DEa{1,2}1'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    WITH T AS (SELECT 'DEaaa1' l FROM dual
    UNION SELECT 'DEaa1' l FROM dual
    UNION SELECT 'DEa1bbb' l FROM dual
    UNION SELECT 'MonDEaa1bbb' l FROM dual
    )
    SELECT *
    FROM t
    WHERE REGEXP_LIKE ( l, 'DEa{1,2}1') 
     
    Résultat
    DEa1bbb => Oui car DEa1 est présent, les bbb regexp s en moque
    DEaa1 => Ok
    MonDEaa1bbb => Oui car DEaa1 est présent

    Donc mettre ^ pour début et $ pour fin est TOTALEMENT correct si tu veux limiter aux chiffres de 3 à 5 nombres.

    Je rajouterai une petite réserve sur l'utilisation d'un nombre en paramètre de regexp_like, attention aux transformations implicites NUMBER->VARCHAR
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  3. #3
    Membre émérite
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Avril 2013
    Messages
    1 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 993
    Points : 2 499
    Points
    2 499
    Par défaut
    Salut McM, je te remercie pour ta réponse.

    Je suis cependant troublé par ce que tu écris car si je vais sur le site d'Oracle "https://docs.oracle.com/cd/B19306_01...fns_regexp.htm" je lis :

    {m} Interval—Exact Count
    Matches exactly m occurrences of the preceding subexpression.
    The expression a{3} matches the strings aaa, but does not match aa.

    {m,} Interval—At Least Count
    Matches at least m occurrences of the preceding subexpression.
    The expression a{3,} matches the strings aaa and aaaa, but does not match aa.

    {m,n}
    Interval—Between Count
    Matches at least m, but not more than n occurrences of the preceding subexpression.
    The expression a{3,5} matches the strings aaa, aaaa, and aaaaa, but does not match aa.

    Ce qui me gêne c'est que les exemples donnés ne parlent pas de mon cas, à savoir que si je met {3} Oracle va ramener 1234 par exemple. Idem pour l'exemple avec {3,5}; à croire qu'Oracle veut cacher cette partie qui est difficile à saisir intuitivement.

    En outre, il est marqué : {m} Matches exactly m occurrences of the preceding subexpression.
    Pour moi, le mot "Exactement" signifie quelque chose de bien précis : 3 exactement c'est ni deux ni quatre mais SEULEMENT 3.

    Pour {m,n]}, il est marqué "Matches at least m, but not more than n occurrences of the preceding subexpression."
    Je ne rêve pas, il est marqué "no more" donc si je mets {2,5} je ne dois pas récupérer 123456 et pourtant c'est ce qui se passe.
    DBA Oracle
    Rédacteur du blog : dbaoraclesql.canalblog.com

  4. #4
    McM
    McM est déconnecté
    Expert éminent

    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Juillet 2003
    Messages
    4 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Juillet 2003
    Messages : 4 580
    Points : 7 740
    Points
    7 740
    Billets dans le blog
    4
    Par défaut
    Je ne rêve pas, il est marqué "no more" donc si je mets {2,5} je ne dois pas récupérer 123456 et pourtant c'est ce qui se passe.
    Non, comme je l'ai dit et expliqué, la base des expressions régulières c'est de rechercher du texte à l'intérieur d'une chaine.

    'DEaaa1' n'est pas ramené dans mon exemple ('DEa{1,2}1') car il y a 3 'a'.

    Si tu fais regexp_like('ab01cd', '[0-9]') tu t'attends à quel résultat ? Vrai ou Faux ?
    Si tu fais regexp_like('ab01cd', '[0-9]{1}') tu t'attends à quel résultat ? Vrai ou Faux ?
    Si tu fais regexp_like('ab01cd', '[0-9]{1,1}') tu t'attends à quel résultat ? Vrai ou Faux ?


    Les 3 regexp que j'ai donné sont identiques, ils font tous la même chose, c'est à dire recherche la présence d'un chiffre dans la chaine.
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  5. #5
    Membre émérite
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Avril 2013
    Messages
    1 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 993
    Points : 2 499
    Points
    2 499
    Par défaut
    Hmmmmmmmmmmm, je ne suis pas entièrement satisfait, ça reste encore confus mais je n'arrive pas à mieux expliquer le problème.
    En tout cas merci pour ta réponse McM.
    DBA Oracle
    Rédacteur du blog : dbaoraclesql.canalblog.com

  6. #6
    Membre émérite
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Avril 2013
    Messages
    1 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 993
    Points : 2 499
    Points
    2 499
    Par défaut
    Bon, une des qualités de base de tout bon informaticien qui se respecte est de ne JAMAIS laisser un problème non résolu!

    Par rapport à mon souci ci-dessus, j'ai enfin compris que {n} trouvait une occurrence de n caractères dans une chaine de caractères et seulement le nombre précisé MAIS cette occurrence de n caractères peut elle même être dans une chaîne plus grande.

    J'ai donc utilisé la fonction REGEXP_SUBSTR et REGEX_REPLACE pour bien voir qu'est-ce que Oracle récupère comme chaine.
    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
     
    SQL> select nom, salaire, regexp_substr(salaire, '[[:digit:]]{3}') "SUBSTR 3", regexp_substr(salaire, '[[:digit:]]{3,5}') "SUBSTR 3 5", regexp_replace(salaire, '[[:digit:]]{3}', '999') "REPLACE 3"from emp
     
    NOM                     SALAIRE SUBSTR 3                       SUBSTR 3 5                     REPLACE 3
    -------------------- ---------- ------------------------------ ------------------------------ ------------------------------
    1A
    2258AA
    DUBOIRES                      1                                                               1
    DDDUBOIRES                   12                                                               12
    FRADUBOIS                   123           123                            123                            999
    DDFRADDUBOISDD             1234      123                            1234                           9994
    DUBOIS                    80500            805                            80500                          99900
    DUBOI                     80500            805                            80500                          99900
    DUBOIS                   200000           200                            20000                          999999
    TIMBAUT                  100000          100                            10000                          999999
    DUBOIS12EAU              200000       200                            20000                          999999
    DUBOI                    200000           200                            20000                          999999
    DUBOISEAU                 80500         805                            80500                          99900
    TIMBAUD                  100000         100                            10000                          999999
    TIMBAUT                  100000         100                            10000                          999999
    TIMBAUT                  200000         200                            20000                          999999
    TIMBAUT                   88500          885                            88500                          99900
    TIMBAUT                   77800          778                            77800                          99900
    TIMBAUT                   77800          778                            77800                          99900
    TIMBAUT                   80500          805                            80500                          99900
    DRABAUD                   88500          885                            88500                          99900
    DRABAUD                   88500          885                            88500                          99900
    DRABAUD                  100000         100                            10000                          999999
    DRABAUD                  100000         100                            10000                          999999
    DRABAUD                  210500         210                            21050                          999999
    DUBOIS                   200000          200                            20000                          999999
    DUBOIS                   200000          200                            20000                          999999
    dUBOIS                   110000          110                            11000                          999999
    a aab aaabc aaaabcd       80500     805                            80500                          99900
     
    29 rows selected.
    Ce qui me plait dans ce résultat c'est que suivant que je mets {3} ou {3,5} le résultat n'est pas le même; j'ai bien 3 caractères et SEULEMENT 3 avec {3}

    En revanche ce qui m'embête à nouveau c'est le replace...
    Je fais regexp_replace(salaire, '[[:digit:]]{3}', '999') et j'ai 123 qui devient 999 OK, 1234 qui devient 9994 OK car seuls les trois premiers caractères sont remplacés mais pour 100000 j'ai 999999 au lieu de 999000

    J'en appelle à votre bonté pour m'expliquer ce mystère, surtout que le regexp_substr(salaire, '[[:digit:]]{3}') pour 100000 donne bien 100 donc seuls ces 3 caractères devraient être remplacés.
    DBA Oracle
    Rédacteur du blog : dbaoraclesql.canalblog.com

  7. #7
    McM
    McM est déconnecté
    Expert éminent

    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Juillet 2003
    Messages
    4 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Juillet 2003
    Messages : 4 580
    Points : 7 740
    Points
    7 740
    Billets dans le blog
    4
    Par défaut
    Tout simplement parce que le replace est sur toutes les occurrences par défaut : http://docs.oracle.com/cd/B19306_01/...nctions130.htm
    Si tu ne veux remplacer que la première occurrence, il faut le préciser.
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  8. #8
    Membre émérite
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Avril 2013
    Messages
    1 993
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 993
    Points : 2 499
    Points
    2 499
    Par défaut
    Ok, c'est bon Mcm, cette fois j'ai compris
    Merci pour toute ton aide!
    DBA Oracle
    Rédacteur du blog : dbaoraclesql.canalblog.com

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

Discussions similaires

  1. Réponses: 5
    Dernier message: 08/06/2010, 14h11
  2. fonction qui ne marche pas
    Par Donatelli dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 30/07/2007, 14h03
  3. Fonction qui ne marche pas
    Par GTJuanpablo dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 14/07/2007, 18h41
  4. Fonction qui ne marche pas sous FireFox
    Par Foudébois dans le forum Général JavaScript
    Réponses: 8
    Dernier message: 17/11/2006, 14h35
  5. requete SQl avec la fonction max () qui ne marche pas
    Par eclipse012 dans le forum Langage SQL
    Réponses: 2
    Dernier message: 09/11/2006, 14h32

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