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

Oracle Discussion :

[tuning] like avec variable


Sujet :

Oracle

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 12
    Points : 5
    Points
    5
    Par défaut [tuning] like avec variable
    Bonjour,
    j'ai un problème de perf sur une de mes requêtes, en particulier sur une whereclause like !

    J'ai 2 tables :
    - table client (codeclient varchar2(20), autrecols varchar2(10)) avec un index sur la colonne codeclient. Cette table contient environ 800k lignes
    - table parametre (param varchar2(512))

    Le fonctionnement de mon outil est le suivant :
    - un ensemble de requête sont prédéfinies dans le code
    - l'utilisateur saisie des paramètres d'entrée (ca peut etre des chaines caractères, des nombres, ... d'où le varchar2 à 512)
    - ces paramètres sont chargés dans la table parametre
    - la requête prédéfinie va faire ensuite des select dans les tables qui vont bien

    Le pb, c'est que l'utilisateur a besoin de mettre des caractères joker (%). Ainsi, il peut rentrer un paramètre DUP% ... ca ira dans ma table client, et retournera DUPONT, DUPOND, DUPOUET, DUPMACHIN ...

    Alors, autant la requête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select c.* from client c where c.codeclient like 'DUP%';
    est instantanée (car elle utilise l'index).
    Autre la requête :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select c.* from client c,parametre p where c.codeclient like p.param;
    est super long; car n'utilise pas l'index, ... et la cardinalité explose.

    J'ai vu que sur interbase, il y a une commande "starting by" qui est un truc qui ressemble au like avec un % à la fin (donc permettant l'utilisation de l'index). Mais en oracle 9i (là ou je suis), il n'y a pas d'équivalent.

    Existe-t-il un moyen de forcer l'index par hint ?!?

    Merci !!!

  2. #2
    Rédacteur

    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 320
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 320
    Points : 3 798
    Points
    3 798
    Par défaut
    Bonsoir ,
    Si on essaye de décoder ton probléme, voila ce que j'en comprends.

    Tu as une reqûete quasi instantané avec un like . Comme le pourcent n'est pas au début il prends l'index existant. Jusque la tout est logique.

    Par contre dans la seconde requête j'ai du mal a comprendre la requête. SI c'est une jointure entre les deux tables pourquoi est ce que tu ne l'écrit tout simplement ainsi :


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select c.* from client c,parametre p where c.codeclient = p.param;
    Si c'est deux colonnes sont indéxes et sous réserve de volumétrie et de cardinalité l'index devrait être utilisé

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 12
    Points : 5
    Points
    5
    Par défaut
    l'égalité ne fonctionne pas si il y a un caractère joker dans mon champ param.
    En gros, je fais :
    insert into parametre (param) values ('DUP%');

    Si je fais:
    select c.* from client c,parametre p where c.codeclient = p.param;
    ==> il me retourne tous les clients qui ont un nom=DUP%
    Or je veux tous les clients qui commencent par DUP.
    C'est bien un like qu'il me faut !

    Ou au pire, je fais :
    select c.* from client c,parametre p where substr(c.codeclient,1,length(p.param)) = p.param;
    mais bon, là encore je casse l'index

  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
    Parce que que dans p.param il y aura le critère de recherche : valeur 'DUP%'

    Ce que je ne comprend pas par contre, c'est combien de ligne contient parametre ? Est ce que l'utilisateur peut renseigner plusieurs critères (mais dans ce cas les critères sont des OR et non des AND).

    Bref si une seule ligne, est ce que
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT c.* FROM client c
    WHERE c.codeclient LIKE (SELECT p.param FROM parametre p)
    va mieux ?
    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
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 12
    Points : 5
    Points
    5
    Par défaut
    j'essaie ca demain au boulot pour voir.
    La table parametre ne contiendra que peu d'enregistrement (<20)

    et c'est bien un comportement de 'OR' pour la requête. C'est en emplacement d'une requête qui était construite en dynamique :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    select c.* from client c
    where c.codeclient like 'DUP%' or
              c.codeclient like 'DUR%' or
              c.codeclient like 'MARTIN%' or
              c.codeclient like 'MACHIN%' ;
    qui était bcp moins flexible pour certains besoins.

    Au niveau du plan d'exécution, vaut-il mieux un nested loop ou un hash join ? J'ai du mal à trouver des infos en francais sur ce sujet

  6. #6
    Rédacteur

    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    2 320
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 320
    Points : 3 798
    Points
    3 798
    Par défaut
    batin merci d'utilise les balises Quote et Code qui améliorent la lisibilité de tes posts



    Par contre concernant la jointure cela dépend totalement de ta volumtrie et du nombre de lignes ramenés.

    Pourquoi ne pas laisser Oracle choisir en s'assurant juste qu'il posséde tous les paramétres pour prendre la meilleure jointure

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mars 2006
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2006
    Messages : 12
    Points : 5
    Points
    5
    Par défaut marche toujours pas
    J'ai essayé le code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT c.* FROM client c 
    WHERE c.codeclient LIKE (SELECT p.param FROM parametre p)
    mais ca ne marche pas ...
    ORA-1427 : single-row subquery returns more than one row !

    Y a une autre solution ?

  8. #8
    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
    Essayes de mettre un HINT
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    SELECT /*+ INDEX(c NOM_INDEX)*/ c.* 
    FROM client c,parametre p 
    WHERE c.codeclient = p.param;
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Avril 2005
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2005
    Messages : 36
    Points : 32
    Points
    32
    Par défaut
    Bonsoir,

    etant donné la conception de ton application, je ne vois pas d'autre solution que de faire un cuseur sur ta table parametre pour construire dynamiquement ta requete et l'envoyer par un EXECUTE IMMEDIATE.

  10. #10
    Expert éminent sénior
    Avatar de orafrance
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    15 967
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Points : 19 075
    Points
    19 075
    Par défaut
    Sinon, il est possible de forcer le plan d'exécution grâce aux outlines

    Sinon, j'arrive pas bien à saisir pourquoi tu ne fais pas un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select c.* from client c where c.codeclient like p_mon_parametre || '%';
    où p_mon_parametre est la valeur de début, ici DUP

  11. #11
    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 qu'il peut y avoir plusieurs p_mon_parametre .
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  12. #12
    Expert confirmé
    Avatar de laurentschneider
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2005
    Messages
    2 944
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2005
    Messages : 2 944
    Points : 4 926
    Points
    4 926
    Par défaut
    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
     
    SQL> select /*+ RULE */ * from emp where ename like :p||'%';                                                                                                                    
     
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 2775231443
     
    --------------------------------------------
    | Id  | Operation                   | Name |
    --------------------------------------------
    |   0 | SELECT STATEMENT            |      |
    |   1 |  TABLE ACCESS BY INDEX ROWID| EMP  |
    |*  2 |   INDEX RANGE SCAN          | I    |
    --------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
     
       2 - access("ENAME" LIKE :P||'%')
           filter("ENAME" LIKE :P||'%')
     
    Note
    -----
       - rule based optimizer used (consider using cbo)
    où est le problème?

  13. #13
    Expert confirmé
    Avatar de laurentschneider
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2005
    Messages
    2 944
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2005
    Messages : 2 944
    Points : 4 926
    Points
    4 926
    Par défaut
    Citation Envoyé par McM
    parce qu'il peut y avoir plusieurs p_mon_parametre .
    ouais, dans ce cas il peut y avoir des plans où il vaudrait mieux employer l'index et d'autres où non. Si tu employes des bind, tu gagnes en parsing, mais tu perds en flexibilité...

  14. #14
    Expert confirmé
    Avatar de laurentschneider
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2005
    Messages
    2 944
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2005
    Messages : 2 944
    Points : 4 926
    Points
    4 926
    Par défaut
    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
     
    SQL> exec :p := '%T%'
    PL/SQL procedure successfully completed.
     
    P
    --------------------------------------------------------------------------------------------------------------------------------
    %T%
     
    SQL> select * from emp where ename like :p;
         EMPNO ENAME      JOB              MGR HIREDATE                   SAL       COMM     DEPTNO
    ---------- ---------- --------- ---------- ------------------- ---------- ---------- ----------
          7654 MARTIN     SALESMAN        7698 28.09.1981 00:00:00       1250       1400         30
          7788 SCOTT      ANALYST         7566 19.04.1987 00:00:00       3000                    20
          7369 SMITH      CLERK           7902 17.12.1980 00:00:00        800                    20
          7844 TURNER     SALESMAN        7698 08.09.1981 00:00:00       1500          0         30
     
     
    Execution Plan
    ----------------------------------------------------------
    Plan hash value: 2775231443
     
    ------------------------------------------------------------------------------------
    | Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
    ------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT            |      |     2 |    74 |     1   (0)| 00:00:01 |
    |   1 |  TABLE ACCESS BY INDEX ROWID| EMP  |     2 |    74 |     1   (0)| 00:00:01 |
    |*  2 |   INDEX RANGE SCAN          | I    |     2 |       |     1   (0)| 00:00:01 |
    ------------------------------------------------------------------------------------
     
    Predicate Information (identified by operation id):
    ---------------------------------------------------
     
       2 - access("ENAME" LIKE :P)
           filter("ENAME" LIKE :P)
    ça me parait dangereux comme approche...

  15. #15
    Expert confirmé
    Avatar de laurentschneider
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2005
    Messages
    2 944
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2005
    Messages : 2 944
    Points : 4 926
    Points
    4 926
    Par défaut
    je n'avais pas bien compris le problème. p n'est pas un bind mais une colonne. dans ce cas, je ne pense pas qu'il soit judicieux de forcer l'usage d'un index. mais si vraiment les paramètres sont toujours de la forme 'XXX%', un hint peut être "évalué"

  16. #16
    Expert éminent sénior
    Avatar de orafrance
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    15 967
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Points : 19 075
    Points
    19 075
    Par défaut
    Citation Envoyé par batin
    Si je fais:
    select c.* from client c,parametre p where c.codeclient = p.param;
    ==> il me retourne tous les clients qui ont un nom=DUP%
    Or je veux tous les clients qui commencent par DUP.
    C'est bien un like qu'il me faut !
    pourquoi pas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select c.* from client c,parametre p where c.codeclient like p.param||'%'
    Eventuellement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    select c.* from client c, (select replace(param,'%','') from parametre) p
    where c.codeclient like p.param||'%'

  17. #17
    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
    Mais si les paramètres possibles sont '%DUP%' ou 'DU%ON%' ces requetes ne marcheront pas.

    Dans quoi cette requete va s'effectuer ? (report, pl/sql, ecran...)
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  18. #18
    Expert éminent sénior
    Avatar de orafrance
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    15 967
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Points : 19 075
    Points
    19 075
    Par défaut
    de toutes façons si le début de la recherche n'est pas connu on passera pas par l'index. Lorsqu'il s'agit de faire une recherche dans des gros volumes, il est essentiel d'interdire les recherches tout azimut; le début de la chaîne de caractères doit être indiqué.

  19. #19
    Expert confirmé
    Avatar de laurentschneider
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Décembre 2005
    Messages
    2 944
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2005
    Messages : 2 944
    Points : 4 926
    Points
    4 926
    Par défaut
    Citation Envoyé par Fred_D
    de toutes façons si le début de la recherche n'est pas connu on passera pas par l'index.
    ou plutôt, on "ne devrait pas passer" par l'index...


    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
     
    SQL> create table t as select 'ABCDEF' c, 1 x from dual;
     
    Table created.
     
    SQL> create table p as select '%C%' p  from dual;
     
    Table created.
     
    SQL> create index i on t(c);
     
    Index created.
     
    SQL> set autot on exp
    SQL> select t.* from t,p where c like p||'%';
    C               X
    ------ ----------
    ABCDEF          1
     
     
    Execution Plan
    ----------------------------------------------------------
       0      SELECT STATEMENT Optimizer=CHOOSE
       1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T'
       2    1     NESTED LOOPS
       3    2       TABLE ACCESS (FULL) OF 'P'
       4    2       INDEX (RANGE SCAN) OF 'I' (NON-UNIQUE)

    ci dessus, l'usage d'un index n'est pas bon...

  20. #20
    Expert éminent sénior
    Avatar de orafrance
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    15 967
    Détails du profil
    Informations personnelles :
    Âge : 47
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Points : 19 075
    Points
    19 075
    Par défaut
    C'est normal ça... ta chaîne de caractère contient '%' en début de chaîne et l'index est construit sur ce début de chaîne. On a heureusement le droit d'indexer les colonnes qui commence par %

    En revanche si tu essayes :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select t.* from t,p where c like '%'||p||'%';
    Tu devrais faire un FULL SCAN

Discussions similaires

  1. Syntaxe de concaténation dans un LIKE avec variable
    Par pasc06 dans le forum Langage
    Réponses: 5
    Dernier message: 28/12/2008, 16h55
  2. syntaxe like avec variable
    Par xirom dans le forum Requêtes
    Réponses: 3
    Dernier message: 23/09/2008, 22h56
  3. [1.x] Syntaxe d'une requete sql like avec variable
    Par Ryo_Saeba dans le forum Symfony
    Réponses: 2
    Dernier message: 15/04/2008, 15h32
  4. Utilisation de like avec une variable en VBA
    Par mawi dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 11/01/2008, 12h47
  5. clause like avec variable
    Par youp_db dans le forum SQL
    Réponses: 6
    Dernier message: 11/06/2007, 09h40

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