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

PL/SQL Oracle Discussion :

Sous-requête utilisée plusieurs fois : Comment éviter ?


Sujet :

PL/SQL Oracle

  1. #1
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut Sous-requête utilisée plusieurs fois : Comment éviter ?
    Bonjour à tous,

    J'ai une requête de ce type qui me sert de curseur :

    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
     
    select produit, jour, valeur
    from
    (
      select produit, jour, sum(ventes) valeur
      from tabledesventes
      where jour between sysdate - 28 and sysdate
      group by produit, jour
    ) t1
    where valeur < (
      select avg(valeur)
      from (
        select sum(ventes) valeur
        from tabledesventes
        where produit = t1.produit
        and jour between sysdate - 28 and sysdate
      )
    ) * 5
    or 1 > (
      select avg(valeur)
      from (
        select sum(ventes) valeur
        from tabledesventes
        where produit = t1.produit
        and jour between sysdate - 28 and sysdate
      )
    )
    On voit clairement que j'ai deux fois exactement la même sous-requête sur tabledesventes (dans la clause where).

    Et une troisième fois similaire dans la clause from.

    D'un point de vue performances, Oracle a l'air de retrouver ses petits.

    Mais d'un point de vue lisibilité (car vous vous en doutez, j'ai fais la version courte...) c'est pas terrible.

    J'ai été tenté de créer une vue :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    select produit, jour, sum(ventes) valeur
    from tabledesventes
    where jour between sysdate - 28 and sysdate
    group by produit, jour
    Mais je préfère éviter, dans la mesure où je voudrais éviter d'avoir 3 ou 4 vues par procédure stockées... (et dans ce cas, pourquoi ne pas faire aussi une vue pour la requête entière).

    Je me demande alors quelles autres solutions s'offrent à mois...

    Je suis tenté par le "with", mais est-ce que ça marche pour un curseur ?

    Je pense à un truc du genre :

    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
     
    cursor c is
    with ventes_28_jours as (
      select produit, jour, sum(ventes) valeur
      from tabledesventes
      where jour between sysdate - 28 and sysdate
      group by produit, jour
    ),
    moyene_28_jour as (
      select produit, avg(valeur) valeur
      from ventes_28_jours
      group by produit
    )
    select v1.produit, v1.jour, v1.valeur
    from ventes_28_jours v1
    where v1.valeur < (
      select v2.valeur
      from moyenne_28_jours v2
      where v2.produit = v1.produit
    ) * 5
    or 1 > (
      select v2.valeur
      from moyenne_28_jours v2
      where v2.produit = v1.produit
    )
    Est-ce qu'il existe d'autres astuces du genre qui permettent de réduire le code (et améliorer les perfs) ?

  2. #2
    Expert confirmé Avatar de mnitu
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    5 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2007
    Messages : 5 611
    Par défaut
    Le with marche avec curseur, avez vous testé ?
    Avg sous sa forme analytique devrait permettre de simplifier la requête.

  3. #3
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    J'avais testé, mais avec des erreurs

    Je viens de réussir à compiler effectivement, et cette fois ça marche.

    Par contre, l'algo est finalement bien plus complexe (bouh snif)

    Ca marche, mais par contre est d'une lenteur abominable.

    Je me souviens que les fonction analytiques pouvaient servir dans certains cas, mais là, je sèche... Pourtant, je suis certain que dans un cas précis, ça marche... si vous pouviez me rafraîchir la mémoire

    Il s'agit de retrouver un champ qui se trouve sur la même ligne que celle qui contient le max d'un champ...

    En gros, simplifier ce genre de code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    select min(madate)
    from matable
    where mavaleur = (select max(mavaleur) from matable)

  4. #4
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    En fait, le but du jeu, c'est d'éviter que ce truc mette 15 ans à tourner, et c'est mal parti :/
    => 1 minute pour... ne rien faire (j'ai aucune donnée dans MSK qui rentre dans le critère)

    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
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
     
    CREATE OR REPLACE PROCEDURE SOC1."MOY_VENDUE_LISSEE" as
    begin
    declare
     
    cursor c is
    with
      vente_28_jours as (
        select msk.codsoc, msk.sigdep, msk.codpro, msk.datmvt, sum(msk.qtemvt) somme
        from msk
        inner join pro on pro.codsoc = msk.codsoc and pro.codpro = msk.codpro and pro.codblocage in('VC','VN')
        where msk.codsoc = 1 and msk.codosk = 'VMAG'
        and msk.datmvt between to_char((sysdate - 28),'YYYYMMDD') and to_char(sysdate,'YYYYMMDD')
        group by msk.codsoc, msk.sigdep, msk.codpro, msk.codosk, msk.datmvt
      ),
      pic as (
        select v.codsoc, v.sigdep, v.codpro, max(v.somme) valmax, avg(v.somme) valmoy
        from vente_28_jours v
        group by v.codsoc, v.sigdep, v.codpro
      ),
      datmax as (
        select v.codsoc, v.sigdep, v.codpro, min(v.datmvt) datmin
        from vente_28_jours v
        inner join pic p on p.codsoc = v.codsoc and p.sigdep = v.sigdep and p.codpro = v.codpro and p.valmax = v.somme and p.valmax >= p.valmoy * 5 or p.valmoy <= 1
        group by v.codsoc, v.sigdep, v.codpro 
      )
    select v.codsoc, v.sigdep, v.codpro, sum(v.somme) / decode(d.datmin, null, 28, 27) somme
    from vente_28_jours v
    left outer join datmax d on d.codsoc = v.codsoc and d.sigdep = v.sigdep and d.codpro = v.codpro
    where v.datmvt != d.datmin
    group by v.codsoc, v.sigdep, v.codpro, d.datmin
    order by v.codsoc, v.sigdep, v.codpro;
     
    cc c%rowtype;
    cpt number(10);
     
     
     
    begin
     
      update dsk
      set c15=0
      where codsoc=1;
     
    commit;
     
    cpt:=0;
    open c;
      loop
      fetch c into cc;
      exit when c%notfound;
     
      cpt:=cpt+1;
     
      --commit;
     
      update dsk
      set c15=cc.somme
      where codsoc=cc.codsoc
      and sigdep=cc.sigdep
      and codpro=cc.codpro;
     
      if cpt=1000 then 
      commit;
      cpt:=0;
      end if;
     
      end loop;
     
      close c;
    commit;
     
    end;
    end;
    (oui oui, je sais, quand j'en fait, c'est de la bonne qui colle aux pieds...)

  5. #5
    McM
    McM est déconnecté
    Expert confirmé

    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
    Billets dans le blog
    4
    Par défaut
    Le OR me parait bizarre sans les (), j'ai toujours du mal à connaitre la priorité
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    INNER JOIN pic p ON p.codsoc = v.codsoc AND p.sigdep = v.sigdep 
    AND p.codpro = v.codpro AND p.valmax = v.somme 
    AND p.valmax >= p.valmoy * 5 OR p.valmoy <= 1
    Après un test, il y a bien un souci sur cette jointure. Les AND sont prioritaires sur le OR.. donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    INNER JOIN pic p ON p.codsoc = v.codsoc AND p.sigdep = v.sigdep
    AND p.codpro = v.codpro AND p.valmax = v.somme 
    AND (p.valmax >= p.valmoy * 5 OR p.valmoy <= 1)

  6. #6
    Expert confirmé
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 197
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 197
    Billets dans le blog
    1
    Par défaut
    A la fin de la journée, j'avais effectivement remarqué qu'il y avait une différence de résultat entre la requête originale et la finale.

    Je devais regarder cet après-midi d'où ça venait (super de bosser sur plusieurs projets en même temps...)

    Merci d'avoir trouvé ce bug, effectivement c'est un oubli

Discussions similaires

  1. [MySQL] Comment puis faire qu'une requete MySQL et utiliser plusieur fois mysql_fetch_assoc()
    Par pierrot10 dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 24/02/2015, 03h54
  2. [MySQL] Utiliser plusieurs fois la même requête
    Par zozoman dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 29/08/2013, 10h43
  3. Comment créer une sous requête sur plusieurs champs?
    Par Anonymouse dans le forum Requêtes et SQL.
    Réponses: 4
    Dernier message: 10/06/2008, 09h13
  4. Réponses: 5
    Dernier message: 24/08/2006, 15h00
  5. Sous-requête excutée plusieurs fois dans une requête
    Par sheridan31 dans le forum Oracle
    Réponses: 8
    Dernier message: 03/07/2006, 16h18

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